Android自定义View实现遥控器按钮
作者:z真真 发布时间:2021-12-27 09:50:47
标签:Android,遥控器,按钮
本文实例为大家分享了Android自定义View实现遥控器按钮的具体代码,供大家参考,具体内容如下
效果图:
原理:
onSizeChanged拿到控件宽高,进行path和region的计算(此处,path和region的坐标值都是以viewWidth/2,viewHeight/2为坐标原点进行计算的)
画布平移,绘制5个path
点击事件,判断是否处于相应的region区域内,进行控件的重绘
点击事件motionEvent的原始坐标(getX和getY),是以viewParent的左上角为坐标原点的,需要经过matrix转换成以控件中心点为原点的坐标体系。
Region区域,paint的style设置为stroke模式,遍历绘制
mPaint.setColor(Color.RED);
RegionIterator iterator = new RegionIterator(topRegion);
?Rect r = new Rect();
?while (iterator.next(r)) {
? ? ? canvas.drawRect(r, mPaint);
?}
源码:
public class RemoteControlMenu extends View {
? ? private int mWidth;
? ? private int mHeight;
? ? private RectF bigRectF;
? ? private int bigRadius;
? ? private RectF smallRectF;
? ? private int smallRadius;
? ? private int padding = 20;
? ? private int sweepAngel = 80;
? ? private int offsetAngel;
? ? @TouchArea
? ? private int mTouchArea = TouchArea.INVALID;
? ? private Paint mPaint;
? ? private Region topRegion, bottomRegion, leftRegion, rightRegion, centerRegion, globalRegion;
? ? private Path topPath, bottomPath, leftPath, rightPath, centerPath, selectedPath;
? ? Matrix mMapMatrix;
? ? private int unselectedColor = 0xff4c5165;
? ? private int selectedColor = 0xffdd9181;
? ? private boolean isSelected = false;
? ? public RemoteControlMenu(Context context) {
? ? ? ? this(context, null);
? ? }
? ? public RemoteControlMenu(Context context, @Nullable AttributeSet attrs) {
? ? ? ? this(context, attrs, 0);
? ? }
? ? public RemoteControlMenu(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
? ? ? ? super(context, attrs, defStyleAttr);
? ? ? ? mPaint = new Paint();
? ? ? ? mPaint.setAntiAlias(true);
? ? ? ? mPaint.setStyle(Paint.Style.FILL);
? ? ? ? mPaint.setStrokeWidth(4);
? ? ? ? mPaint.setColor(unselectedColor);
? ? ? ? offsetAngel = (360 - sweepAngel * 4) / 4;
? ? ? ? bigRectF = new RectF();
? ? ? ? smallRectF = new RectF();
? ? ? ? topRegion = new Region();
? ? ? ? bottomRegion = new Region();
? ? ? ? leftRegion = new Region();
? ? ? ? rightRegion = new Region();
? ? ? ? centerRegion = new Region();
? ? ? ? globalRegion = new Region();
? ? ? ? topPath = new Path();
? ? ? ? bottomPath = new Path();
? ? ? ? leftPath = new Path();
? ? ? ? rightPath = new Path();
? ? ? ? centerPath = new Path();
? ? ? ? mMapMatrix = new Matrix();
? ? }
? ? @Retention(RetentionPolicy.SOURCE)
? ? @IntDef({TouchArea.LEFT, TouchArea.TOP, TouchArea.RIGHT, TouchArea.BOTTOM,
? ? ? ? ? ? TouchArea.CENTER, TouchArea.INVALID})
? ? private @interface TouchArea {
? ? ? ? int LEFT = 1;
? ? ? ? int TOP = 2;
? ? ? ? int RIGHT = 3;
? ? ? ? int BOTTOM = 4;
? ? ? ? int CENTER = 5;
? ? ? ? int INVALID = 0;
? ? }
? ? @Override
? ? public boolean onTouchEvent(MotionEvent event) {
? ? ? ? float[] pts = new float[2];
? ? ? ? pts[0] = event.getX();
? ? ? ? pts[1] = event.getY();
? ? ? ? Log.d("zhen", "原始触摸位置:" + Arrays.toString(pts) + " mMapMatrix: " + mMapMatrix);
? ? ? ? mMapMatrix.mapPoints(pts);
? ? ? ? int x = (int) pts[0];
? ? ? ? int y = (int) pts[1];
? ? ? ? Log.w("zhen", "转换后的触摸位置:" + Arrays.toString(pts) + " mMapMatrix: " + mMapMatrix);
? ? ? ? int touchArea = TouchArea.INVALID;
? ? ? ? switch (event.getAction()) {
? ? ? ? ? ? case MotionEvent.ACTION_UP:
? ? ? ? ? ? ? ? if (leftRegion.contains(x, y)) {
? ? ? ? ? ? ? ? ? ? touchArea = TouchArea.LEFT;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? if (topRegion.contains(x, y)) {
? ? ? ? ? ? ? ? ? ? touchArea = TouchArea.TOP;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? if (rightRegion.contains(x, y)) {
? ? ? ? ? ? ? ? ? ? touchArea = TouchArea.RIGHT;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? if (bottomRegion.contains(x, y)) {
? ? ? ? ? ? ? ? ? ? touchArea = TouchArea.BOTTOM;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? if (centerRegion.contains(x, y)) {
? ? ? ? ? ? ? ? ? ? touchArea = TouchArea.CENTER;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? if (touchArea == TouchArea.INVALID) {
? ? ? ? ? ? ? ? ? ? mTouchArea = touchArea;
? ? ? ? ? ? ? ? ? ? Log.w("zhen", "点击outside");
? ? ? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? ? ? if (mTouchArea == touchArea) {
? ? ? ? ? ? ? ? ? ? ? ? //取消选中
? ? ? ? ? ? ? ? ? ? ? ? isSelected = false;
? ? ? ? ? ? ? ? ? ? ? ? mTouchArea = TouchArea.INVALID;
? ? ? ? ? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? ? ? ? ? //选中
? ? ? ? ? ? ? ? ? ? ? ? isSelected = true;
? ? ? ? ? ? ? ? ? ? ? ? mTouchArea = touchArea;
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? Log.w("zhen", "按钮状态 mTouchArea " + mTouchArea + " isSelected: " + isSelected);
? ? ? ? ? ? ? ? ? ? if (mListener != null) {
? ? ? ? ? ? ? ? ? ? ? ? mListener.onMenuClicked(mTouchArea, isSelected);
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? invalidate();
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? break;
? ? ? ? }
? ? ? ? return true;
? ? }
? ? @Override
? ? protected void onSizeChanged(int w, int h, int oldw, int oldh) {
? ? ? ? super.onSizeChanged(w, h, oldw, oldh);
? ? ? ? mWidth = w;
? ? ? ? mHeight = h;
? ? ? ? //大圆
? ? ? ? bigRadius = (Math.min(mWidth, mHeight) - 250) / 2;
? ? ? ? bigRectF.set(-bigRadius, -bigRadius, bigRadius, bigRadius);
? ? ? ? //小圆
? ? ? ? smallRadius = (bigRadius - padding) / 2;
? ? ? ? smallRectF.set(-smallRadius - padding, -smallRadius - padding,
? ? ? ? ? ? ? ? smallRadius + padding, smallRadius + padding);
? ? ? ? mMapMatrix.reset();
? ? ? ? globalRegion.set(-mWidth / 2, -mHeight / 2, mWidth / 2, mHeight / 2);
? ? ? ? centerPath.addCircle(0, 0, smallRadius, Path.Direction.CW);
? ? ? ? centerRegion.setPath(centerPath, globalRegion);
? ? ? ? float startAngel = -sweepAngel / 2f;
? ? ? ? rightPath.addArc(bigRectF, startAngel, sweepAngel + 4);
? ? ? ? startAngel += sweepAngel;
? ? ? ? rightPath.arcTo(smallRectF, startAngel, -sweepAngel);
? ? ? ? rightPath.close();
? ? ? ? rightRegion.setPath(rightPath, globalRegion);
? ? ? ? startAngel += offsetAngel;
? ? ? ? bottomPath.addArc(bigRectF, startAngel, sweepAngel + 4);
? ? ? ? startAngel += sweepAngel;
? ? ? ? bottomPath.arcTo(smallRectF, startAngel, -sweepAngel);
? ? ? ? bottomPath.close();
? ? ? ? bottomRegion.setPath(bottomPath, globalRegion);
? ? ? ? startAngel += offsetAngel;
? ? ? ? leftPath.addArc(bigRectF, startAngel, sweepAngel + 4);
? ? ? ? startAngel += sweepAngel;
? ? ? ? leftPath.arcTo(smallRectF, startAngel, -sweepAngel);
? ? ? ? leftPath.close();
? ? ? ? leftRegion.setPath(leftPath, globalRegion);
? ? ? ? startAngel += offsetAngel;
? ? ? ? topPath.addArc(bigRectF, startAngel, sweepAngel + 4);
? ? ? ? startAngel += sweepAngel;
? ? ? ? topPath.arcTo(smallRectF, startAngel, -sweepAngel);
? ? ? ? topPath.close();
? ? ? ? topRegion.setPath(topPath, globalRegion);
? ? ? ? Log.d("zhen", "globalRegion: " + globalRegion);
? ? ? ? Log.d("zhen", "globalRegion: " + globalRegion);
? ? ? ? Log.d("zhen", "leftRegion: " + leftRegion);
? ? ? ? Log.d("zhen", "topRegion: " + topRegion);
? ? ? ? Log.d("zhen", "rightRegion: " + rightRegion);
? ? ? ? Log.d("zhen", "bottomRegion: " + bottomRegion);
? ? ? ? Log.d("zhen", "centerRegion: " + centerRegion);
? ? }
? ? @Override
? ? protected void onDraw(Canvas canvas) {
? ? ? ? super.onDraw(canvas);
? ? ? ? canvas.translate(mWidth / 2, mHeight / 2);
? ? ? ? // 获取测量矩阵(逆矩阵)
? ? ? ? if (mMapMatrix.isIdentity()) {
? ? ? ? ? ? canvas.getMatrix().invert(mMapMatrix);
? ? ? ? }
? ? ? ? mPaint.setColor(unselectedColor);
? ? ? ? canvas.drawPath(centerPath, mPaint);
? ? ? ? canvas.drawPath(rightPath, mPaint);
? ? ? ? canvas.drawPath(bottomPath, mPaint);
? ? ? ? canvas.drawPath(leftPath, mPaint);
? ? ? ? canvas.drawPath(topPath, mPaint);
? ? ? ? if (!isSelected) return;
? ? ? ? mPaint.setColor(selectedColor);
? ? ? ? switch (mTouchArea) {
? ? ? ? ? ? case TouchArea.LEFT:
? ? ? ? ? ? ? ? canvas.drawPath(leftPath, mPaint);
? ? ? ? ? ? ? ? break;
? ? ? ? ? ? case TouchArea.TOP:
? ? ? ? ? ? ? ? canvas.drawPath(topPath, mPaint);
? ? ? ? ? ? ? ? break;
? ? ? ? ? ? case TouchArea.RIGHT:
? ? ? ? ? ? ? ? canvas.drawPath(rightPath, mPaint);
? ? ? ? ? ? ? ? break;
? ? ? ? ? ? case TouchArea.BOTTOM:
? ? ? ? ? ? ? ? canvas.drawPath(bottomPath, mPaint);
? ? ? ? ? ? ? ? break;
? ? ? ? ? ? case TouchArea.CENTER:
? ? ? ? ? ? ? ? canvas.drawPath(centerPath, mPaint);
? ? ? ? ? ? ? ? break;
? ? ? ? }
? ? ? ? Log.e("zhen", " touchArea: " + mTouchArea);
? ? ? ? //Android还提供了一个RegionIterator来对Region中的所有矩阵进行迭代,
? ? ? ? // 可以使用该类,获得某个Region的所有矩阵
? ? ? ? //通过遍历region中的矩阵,并绘制出来,来绘制region
// ? ? ? ?mPaint.setColor(Color.RED);
// ? ? ? ?RegionIterator iterator = new RegionIterator(topRegion);
// ? ? ? ?Rect r = new Rect();
// ? ? ? ?while (iterator.next(r)) {
// ? ? ? ? ? ?canvas.drawRect(r, mPaint);
// ? ? ? ?}
//
// ? ? ? ?mPaint.setColor(Color.BLUE);
// ? ? ? ?RegionIterator iterator1 = new RegionIterator(leftRegion);
// ? ? ? ?Rect r1 = new Rect();
// ? ? ? ?while (iterator1.next(r1)) {
// ? ? ? ? ? ?canvas.drawRect(r1, mPaint);
// ? ? ? ?}
//
// ? ? ? ?mPaint.setColor(Color.BLACK);
// ? ? ? ?RegionIterator iterator2 = new RegionIterator(rightRegion);
// ? ? ? ?Rect r2 = new Rect();
// ? ? ? ?while (iterator2.next(r2)) {
// ? ? ? ? ? ?canvas.drawRect(r2, mPaint);
// ? ? ? ?}
//
// ? ? ? ?mPaint.setColor(Color.YELLOW);
// ? ? ? ?RegionIterator iterator3 = new RegionIterator(bottomRegion);
// ? ? ? ?Rect r3 = new Rect();
// ? ? ? ?while (iterator3.next(r3)) {
// ? ? ? ? ? ?canvas.drawRect(r3, mPaint);
// ? ? ? ?}
//
// ? ? ? ?mPaint.setColor(Color.GREEN);
// ? ? ? ?RegionIterator iterator4 = new RegionIterator(centerRegion);
// ? ? ? ?Rect r4 = new Rect();
// ? ? ? ?while (iterator4.next(r4)) {
// ? ? ? ? ? ?canvas.drawRect(r4, mPaint);
// ? ? ? ?}
? ? }
? ? private MenuListener mListener;
? ? public void setListener(MenuListener listener) {
? ? ? ? mListener = listener;
? ? }
? ? // 点击事件 *
? ? public interface MenuListener {
? ? ? ? void onMenuClicked(int type, boolean isSelected);
? ? }
}
来源:https://blog.csdn.net/ZHENZHEN9310/article/details/95364272


猜你喜欢
- 在程序中对文件操作是非常常见的,而对文件的操作则不可避免的需要文件的路径,并对文件的路径进行一系列的操作,例如:判断已知的路径是一个目录还是
- 网上android播放器虽然挺多,感觉提供的歌词显示功能比较死板,要么搜索给的条件死死的,要么放置sdcard内部的歌词格式需要统一,应该提
- 1)下载sqlite jdbc驱动http://www.xerial.org/maven/repository/artifact/org/x
- 1.string是引用类型还是值类型MSDN官方说string是引用类型;引用类型:引用分配栈内存,引用类型本身的数据存储在堆中;值类型:在
- 简介JSR-303 是 JAVA EE 6 中的一项子规范,叫做 Bean Validation。在任何时候,当你要处理一个应用程序的业务逻
- 阅读提示 具有mybatis基础,熟练使用mybatis-plus。概述 我们都知道,mybatis-plus是一个mybatis的增强
- Android ListView的优化,在做Android项目的时候,在用到ListView 界面及数据显示,这个时候如果资源过大,对项目来
- 测试代码pom.xml:<?xml version="1.0" encoding="UTF-8"
- 前言最近因为同事bean配置的问题导致生产环境往错误的redis实例写入大量的数据,差点搞挂redis。经过快速的问题定位,发现是同事新增一
- 现在的应用在注册登录或者修改密码中都用到了短信验证码,那在android中是如何实现获取短信验证码并自动填写的呢?首先,需要要在manife
- 一、this关键字的作用this关键字除了可以强调本类中的方法还具有以下作用。1.表示类中的属性2.可以使用关键字调用本类中的构造方法3.t
- 前言什么是mybatis二级缓存?二级缓存是多个sqlsession共享的,其作用域是mapper的同一个namespace。即,在不同的s
- 本章内容密码加密方式怎么升级?spring security底层怎么实现的密码加密方式升级?密码加密方式怎么升级?前面我们学过Delegat
- Android 双击返回键退出程序的方法总结下面先说说LZ思路,具体如下: 1. 第一种就是根据用户点击俩次的时间间隔去判断是否退出程序;
- 本文实例为大家分享了Java实现抢红包功能的具体代码,供大家参考,具体内容如下关键思想:1.抢红包涉及多人并发操作,需要做好同步保证多线程运
- 最近项目用到txt文件和xls文件的转换,这里记录一下具体的思路。下面利用java代码实现txt转xls,这里要使用到jxl.jar包,这个
- 今天实现一个很多app中使用到的加载进度条的效果,可能我们平时数据加载都使用到的是系统自带的,但是也有很多app加载进度条的效果实现挺好看,
- SpringBoot默认使用HikariDataSource数据源定义数据源:存储了所有建立数据库连接的信息。通过提供正确的数据源名称,你可
- java 回调机制的实例详解序言最近接触到了回调机制(CallBack)。初识时感觉比较混乱,而且在网上搜索到的相关的讲解,要么一言带过,要
- 问题描述输入一个链表,输出该链表中倒数第k个结点。(尾结点是倒数第一个)结点定义如下:public class ListNode { &nb