摘要:自定义控件这一块,今天我就教大家如何来自定义一个无人飞行器的固定摇杆控件,摇杆是固定在手机屏幕的固定位置,点击的对应区域高亮,并且实时对飞行器的轨迹控制。

一丶飞行器的介绍,现在有很多无人飞行器,通过wifi或者是遥控器等来实现无人机的通讯,达到远程操控无人飞行器的目的。
飞行器的操作方式大致可以分为两类:美国手和日本手。
美国手:

  • 左手:(上下:油门升降 左右:左旋右旋)
  • 右手:(上下:前进后退 左右:左移右移)

日本手:

  • 左手:(上下:前进后退 左右:左旋右旋)
  • 右手:(上下:油门升降 左右:左移右移)

二丶飞行器摇杆的实现

  1. 初始化图片资源,定义点击的区域路径,首先通过中心位置,图片半径,定义可以被点击的四个Path区域,以及中间触摸无效的区域.
  private void init() {setLayerType(View.LAYER_TYPE_SOFTWARE, null);paint = new Paint();
//        titleHeight = UiUtil.Dp2Px(30);Resources resources = context.getResources();//背景白色圆环rockerSafeCircle = BitmapFactory.decodeResource(resources, R.mipmap.rocker_safe_circle);float rockerWidth = resources.getDimensionPixelSize(R.dimen.rocker_width);float scaling = rockerWidth / (float) rockerSafeCircle.getWidth();rockerSafeCircle = BitmapUtil.resizeBitmap(rockerSafeCircle, scaling);//未选择状态的扇形rockerSafeSectorUnselected = BitmapUtil.resizeBitmap(BitmapFactory.decodeResource(resources, R.mipmap.rocker_safe_sector_unselected), scaling);//选择状态的扇形rockerSafeSectorSelected = BitmapUtil.resizeBitmap(BitmapFactory.decodeResource(resources, R.mipmap.rocker_safe_sector_selected), scaling);rockerSafeSectorNothing = BitmapUtil.resizeBitmap(BitmapFactory.decodeResource(resources, R.mipmap.weianxia1), scaling);circleYOffset = resources.getDimensionPixelSize(R.dimen.rocket_offset);//四个扇形Path  描述四个扇形区域float radius = rockerSafeCircle.getWidth() / 2;//计算45度扇形的起始点float cos45 = (float) (Math.cos(45) * circleX);rectF = new RectF(-radius, -radius,radius, radius);//四个方向按钮区域left = new Path();left.lineTo(-cos45, cos45);left.addArc(rectF, 135, 90f);left.lineTo(0, 0);top = new Path();top.lineTo(-cos45, -cos45);top.addArc(rectF, 225, 90f);top.lineTo(0, 0);right = new Path();right.lineTo(cos45, -cos45);right.addArc(rectF, 315, 90f);right.lineTo(0, 0);bottom = new Path();bottom.lineTo(cos45, cos45);bottom.addArc(rectF, 45, 90f);bottom.lineTo(0, 0);//各个区域regionLeft = new Region();left.computeBounds(rectF, true);regionLeft.setPath(left, new Region((int) rectF.left, (int) rectF.top, (int) rectF.right, (int) rectF.bottom));regionTop = new Region();top.computeBounds(rectF, true);regionTop.setPath(top, new Region((int) rectF.left, (int) rectF.top, (int) rectF.right, (int) rectF.bottom));regionRight = new Region();right.computeBounds(rectF, true);regionRight.setPath(right, new Region((int) rectF.left, (int) rectF.top, (int) rectF.right, (int) rectF.bottom));regionBottom = new Region();bottom.computeBounds(rectF, true);regionBottom.setPath(bottom, new Region((int) rectF.left, (int) rectF.top, (int) rectF.right, (int) rectF.bottom));//中间不能触摸圆区域float innerRadiux = (float) (radius * 0.4);rectFInner = new RectF(-innerRadiux, -innerRadiux,innerRadiux, innerRadiux);inner = new Path();inner.addCircle(0, 0, (float) (radius * 0.4), Path.Direction.CW);regionInner = new Region();inner.computeBounds(rectFInner, true);regionInner.setPath(inner, new Region((int) rectFInner.left, (int) rectFInner.top, (int) rectFInner.right, (int) rectFInner.bottom));//左右上下操作指示按钮controll_left = BitmapUtil.resizeBitmap(BitmapFactory.decodeResource(resources, R.mipmap.sensor_mode_left_controll_left), scaling);controll_right = BitmapUtil.resizeBitmap(BitmapFactory.decodeResource(resources, R.mipmap.sensor_mode_left_controll_right), scaling);controll_top = BitmapUtil.resizeBitmap(BitmapFactory.decodeResource(resources, R.mipmap.sensor_mode_left_controll_top), scaling);controll_bottom = BitmapUtil.resizeBitmap(BitmapFactory.decodeResource(resources, R.mipmap.sensor_mode_left_controll_bottom), scaling);//安全模式左右上下操作指示按钮safe_controll_left = BitmapUtil.resizeBitmap(BitmapFactory.decodeResource(resources, R.mipmap.safe_mode_left_controll_left), scaling);safe_controll_right = BitmapUtil.resizeBitmap(BitmapFactory.decodeResource(resources, R.mipmap.safe_mode_left_controll_right), scaling);safe_controll_top = BitmapUtil.resizeBitmap(BitmapFactory.decodeResource(resources, R.mipmap.safe_mode_left_controll_top), scaling);safe_controll_bottom = BitmapUtil.resizeBitmap(BitmapFactory.decodeResource(resources, R.mipmap.safe_mode_left_controll_bottom), scaling);}
  1. 绘制图形 首先确定中心位置 绘制中心的白色圆环,通过旋转画布,绘制四个扇形区域图形(选中和未选中),然后再判断左右摇杆,美国手和日本手分别绘制扇形上的图形
 @Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);canvas.translate(circleX, circleY + circleYOffset);  //画布圆心位置确定//绘制白色圆环canvas.drawBitmap(rockerSafeCircle, -rockerSafeCircle.getWidth() / 2, -rockerSafeCircle.getHeight() / 2, paint);canvas.scale(0.98f, 0.98f);    //画布缩放//绘制扇形for (int i = 0; i < 4; i++) {if (i == selectedPosition) {canvas.drawBitmap(rockerSafeSectorSelected, -rockerSafeSectorSelected.getWidth() / 2, -rockerSafeSectorSelected.getHeight() / 2, paint);} else {canvas.drawBitmap(rockerSafeSectorUnselected, -rockerSafeSectorUnselected.getWidth() / 2, -rockerSafeSectorUnselected.getHeight() / 2, paint);}canvas.rotate(90, 0, 0);}//绘制上下左右操作按钮float offset1 = rockerSafeCircle.getWidth() / 2 - controll_left.getWidth() / 2;float offset2 = controll_left.getHeight() / 2;if (isLeft) {if (rockerMode == 1) {canvas.drawBitmap(controll_left, -offset1, -offset2, paint);canvas.drawBitmap(controll_right, offset1 - controll_left.getWidth(), -offset2, paint);canvas.drawBitmap(controll_top, -offset2, -(offset1), paint);canvas.drawBitmap(controll_bottom, -offset2, offset1 - controll_left.getWidth(), paint);} else {canvas.drawBitmap(controll_left, -offset1, -offset2, paint);canvas.drawBitmap(controll_right, offset1 - controll_left.getWidth(), -offset2, paint);canvas.drawBitmap(safe_controll_top, -offset2, -(offset1), paint);canvas.drawBitmap(safe_controll_bottom, -offset2, offset1 - controll_left.getWidth(), paint);}} else {if (rockerMode == 1) {canvas.drawBitmap(safe_controll_left, -offset1, -offset2, paint);canvas.drawBitmap(safe_controll_right, offset1 - controll_left.getWidth(), -offset2, paint);canvas.drawBitmap(safe_controll_top, -offset2, -(offset1), paint);canvas.drawBitmap(safe_controll_bottom, -offset2, offset1 - controll_left.getWidth(), paint);} else {canvas.drawBitmap(safe_controll_left, -offset1, -offset2, paint);canvas.drawBitmap(safe_controll_right, offset1 - controll_left.getWidth(), -offset2, paint);canvas.drawBitmap(controll_top, -offset2, -(offset1), paint);canvas.drawBitmap(controll_bottom, -offset2, offset1 - controll_left.getWidth(), paint);}}}
  1. 设置按钮监听器,当我们持续点击的时候 需要监听当前按下去的区域,以及左右摇杆是否被点击。
 public interface RockerSafeListener {void onRockerClick(int position, boolean isDown);void toggleLeftOrRight(boolean isShow);}
  1. 事件处理,在onTouchEvent(MotionEvent event)时间处理方法中处理手指第一次按下去的区域是否是有效区域,并且高亮,在手指抬起的时候取消高亮,并进行按钮区域的状态监听。
@Overridepublic boolean onTouchEvent(MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN:matchingRegion((int) event.getX(), (int) event.getY());if (effective) {invalidate();
//                    animUtils.valueAnim(this, alpha, 1.0f, 100);if (mListener != null) {mListener.toggleLeftOrRight(false);mListener.onRockerClick(selectedPosition, true);}}break;case MotionEvent.ACTION_UP:if (effective) {invalidate();if (mListener != null) {mListener.toggleLeftOrRight(true);mListener.onRockerClick(selectedPosition, false);}effective = false;}selectedPosition = -1;break;}return true;}//判断点中的点在哪一个区域private void matchingRegion(int x, int y) {x = x - (int) circleX;y = y - titleHeight - (int) circleY; //坐标偏移if (regionInner.contains(x, y))return;if (regionLeft.contains(x, y)) {selectedPosition = 3;effective = true;return;}if (regionTop.contains(x, y)) {selectedPosition = 0;effective = true;return;}if (regionRight.contains(x, y)) {selectedPosition = 1;effective = true;return;}if (regionBottom.contains(x, y)) {selectedPosition = 2;effective = true;return;}}

三丶自定义控件的应用

  1. 自定义一个类,初始化两个摇杆,设置左右摇杆,以及设置监听器。

/***/public class RockerSafeFly {private int rockerMode; //摇杆是什么手  0美国手   1日本手private RockerSafeView left,right;public RockerSafeFly( RockerSafeView left, RockerSafeView right){this.left = left;this.right = right;rockerMode = SpSetGetUtils.getRockerMode();}public void initRockerSafeFly(){left.setIsLeft(true,rockerMode);right.setIsLeft(false,rockerMode);left.setOnSensorLinstener(new RockerSafeView.RockerSafeListener() {@Overridepublic void onRockerClick(int position,boolean isDown) {}@Overridepublic void toggleLeftOrRight(boolean isShow) {}});right.setOnSensorLinstener(new RockerSafeView.RockerSafeListener() {@Overridepublic void onRockerClick(int position,boolean isDown) {}@Overridepublic void toggleLeftOrRight(boolean isShow) {}});}public void dismissRockerSafeFly(){reset();}public void reset(){if(left!=null)left.reset();if(right!=null)right.reset();}
}
  1. 在xml中使用自定义控件。
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"xmlns:app="http://schemas.android.com/apk/res-auto"tools:ignore="MissingConstraints"android:layout_height="match_parent"><com.bird.rockerdome.rocker.RockerSafeViewandroid:id="@+id/rockerSafeView1"android:layout_width="0dp"android:layout_height="match_parent"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent"app:layout_constraintWidth_percent="0.5" /><com.bird.rockerdome.rocker.RockerSafeViewandroid:id="@+id/rockerSafeView2"android:layout_width="0dp"android:layout_height="match_parent"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintTop_toTopOf="parent"app:layout_constraintWidth_percent="0.5" />
</androidx.constraintlayout.widget.ConstraintLayout>
  1. 并在activity中oncreate生命周期中对自定义控件初始化。
 private void intiSafeRocker() {RockerSafeView rockerSafeView1 = findViewById(R.id.rockerSafeView1);RockerSafeView rockerSafeView2 = findViewById(R.id.rockerSafeView2);rockerSafeFly = new RockerSafeFly(rockerSafeView1, rockerSafeView2);rockerSafeFly.initRockerSafeFly();}

四丶结语
自此为止整个自定义控件就已经完全结束了,达到了自己想要的效果,并且根据按下的有效区域,对飞行器发送对应的 控制命令,但是整个控制我们只知道按下去的位置,发送固定的命令,无法确定按下去的值的大小,此种方式的摇杆,只适合新手操作,并且给飞行器发送的杆量只能是很小的值(发送的杆量只能固定值,无法调大小,过大杆量不容易操控,一般发送0.3~0.5的最大杆量)。

一丶Android如何自定义一个固定的飞行操作摇杆?相关推荐

  1. 二丶Android如何自定义一个可移动的飞行虚拟摇杆?

    摘要: 上次我们自定义了一个固定位置摇杆,此摇杆无法调整杆量,且位置固定,这次我们就来定义一个可以自由调整杆量和变换位置的虚拟摇杆. 下面就写一下需要实现此虚拟摇杆的步骤: 一丶初始化资源 我们需要美 ...

  2. android sqlite自定义函数,Android中自定义一个View的方法详解

    本文实例讲述了Android中自定义一个View的方法.分享给大家供大家参考,具体如下: Android中自定义View的实现比较简单,无非就是继承父类,然后重载方法,即便如此,在实际编码中难免会遇到 ...

  3. Android如何自定义一个心电图控件?

    摘要:在我们的日常生活中,通常需要用仪器测量心率数据,来观测身体是否在一个健康的范围之内, 下面的心率图就是用一个胎心仪测量的一个宝宝心率图. 自定义控件如下图所示: 画好这样的一个心电图视图需要自定 ...

  4. Android之自定义一个环形进度控件

    转载请标明出处: http://blog.csdn.net/hai_qing_xu_kong/article/details/53445072 本文出自:[顾林海的博客] ##前言 最近看到一个评分和 ...

  5. 100行Android代码自定义一个流式布局-FlowLayout

    首先来看一下 手淘HD - 商品详情 - 选择商品属性 页面的UI 商品有很多尺码,而且展现每个尺码所需要的View的大小也不同(主要是宽度),所以在从服务器端拉到数据之前,展现所有尺码所需要的行数和 ...

  6. Android自定义一个可伸展的ViewGroup

    /   今日科技快讯   / 近日多家媒体报道,有认证为阿里巴巴集团的员工在职场社交平台称,"88VIP积分将可以免费兑换腾讯视频会员,已经在内部灰度测试,预计双十一前上线".但是 ...

  7. Android中用图片自定义一个进度条(实现蒙板效果)

    问题概述 对于进度条我相信大家不陌生,这里我就不再多说什么了.因为这个不是重点.我们要说的是如何去自定义一个不一样的进度条.这里用到两张图片(背景和前景),其实是三张(背景.前景和蒙图).当我们的蒙图 ...

  8. Android自定义View之图形图像(模仿360的刷新球自定义一个SeekBar)

    概述: 360安全卫士的那个刷新球(姑且叫它刷新球,因为真的不知道叫什么好,不是dota里的刷新球!!),里面像住了水一样,生动可爱,看似简单,写起来不太简单,本例程只是实现了它的部分功能而已,说实话 ...

  9. Android流媒体开发之 自定义一个完备的log模块

    Android音视频开发之 自定义一个完备的log模块 前言 基础知识的掌握 Log系统 为什么需要自定义一个log模块呢? 做什么? 怎么做? 确定成员变量: 初始化LogUtil 输出功能的实现 ...

最新文章

  1. R语言构建xgboost模型:使用xgboost构建泊松回归(poisson regression)模型
  2. 什么是区块链技术?初学者指南
  3. DeepLearning tutorial(4)CNN卷积神经网络原理简介+代码详解
  4. C++函数模板(二)引用
  5. STC89C52 STC89LE52 NRF24L01无线 教程 (一)
  6. mapbox 导航_狂甩不掉,骑行最稳手机支架!一体式安装太方便,秒变单车导航仪...
  7. mysql存储过程中怎么睡几秒_MySql的逻辑架构
  8. 一个封装比较完整的FTP类——clsFTP
  9. matlab虚拟现实仿真
  10. 【解决方案】钉钉直播课堂挂机被点到名字怎么办
  11. 玛莎拉蒂品牌与酷客多小程序达成官方合作!
  12. mysql三台部署_使用三台主机部署LNMP
  13. java 远程登录linux_使用java登录远程LINUX并对服务实现各种操作
  14. C语言学习:C语言编译流程
  15. AVATR阿维塔11维修手册电路图技术资料
  16. 如何在图片上添加边框和文字
  17. 斗鱼直播与熊猫直播竞品分析
  18. 書劍恩仇錄 (無線電視翡翠台版本) - 劇本終回由誰人敲定? 就是李添勝.
  19. day05-表格标签及属性
  20. xp系统远程桌面关闭计算机,WinXP如何打开远程桌面?WinXP打开远程桌面的命令是什么?...

热门文章

  1. Riak CS集群部署
  2. Q1业绩成长超预期,没了左晖的贝壳未来谁能掌舵?
  3. 任正非接受媒体采访 网友:这个老头的回答实力圈粉
  4. 【突破二次元壁】手把手教你用AnimeGAN将风景图转换成宫崎骏动漫风
  5. JavaScript---es6语法---学习笔记
  6. oracle学生成绩表的创建,oracle如何创建视图(1)创建视图V_StudInfo,查询学生的学号,姓名,所修课程号,所修课程名称,成绩等级(9...
  7. java开发用win7好还是win10_Win10和Win7各有优点,大家不要再纠结哪个系统更好了!...
  8. 深度学习第四课——卷积神经网络(week 1)
  9. python中shape[0]与shape[1]
  10. 气缸matlab仿真,基于MATLAB的气缸运动建模与仿真