之前弄过一个,但都是复制来的,经过上一篇的学习,这次自己用kotlin写一个
自定义 View
kotlin
class RemoteControlMenu: View {
private lateinit var paint: Paint
private val buttonNum: Int = 4 //按钮数量
private val offsetAngel: Float = 10f; //每个按键之间的距离为10°,要求offsetAngel*buttonNum<=360
private val sweepAngel: Float = (360 - offsetAngel * buttonNum) / buttonNum // 计算每个按键的角度
private var bigCircleOuterRadius: Float = 300f // 上下左右按键外半径,300f默认值
private var bigCircleInnerRadius: Float = 200f // 上下左右按键内半径,200f默认值
private var enterCircleRadius: Float = 150f // ok按键圆半径,150f默认值
private val unselectedColor: Int = Color.RED // 未点击时的按键颜色
private val selectedColor: Int = Color.YELLOW // 点击时的按键颜色
private lateinit var buttonRegionList: ArrayList<Region>
private lateinit var buttonPathList: ArrayList<Path>
private var previousTouchArea: Int = -1 //上一个触碰的区域
private var touchArea: Int = -1 //触碰的区域
private var myOnClickListener: MyOnClickListener? = null //回调方法
constructor(context: Context): this(context = context, attributeSet = null)
constructor(context: Context, attributeSet: AttributeSet?): this(context = context,attributeSet = attributeSet,defStyleAttr = 0)
constructor(context: Context, attributeSet: AttributeSet?, defStyleAttr: Int): super(context,attributeSet,defStyleAttr){
paint = Paint()
paint.color = unselectedColor
paint.style = Paint.Style.FILL
paint.strokeWidth = 3f;
buttonPathList = ArrayList<Path>(buttonNum+1)
buttonRegionList = ArrayList<Region>(buttonNum+1)
}
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
val canvasWidth: Int = w // 画布长宽
val canvasHeight: Int = h
bigCircleOuterRadius = w.coerceAtMost(h).toFloat()/2 // 计算圆的半径
bigCircleInnerRadius = bigCircleOuterRadius*2/3
enterCircleRadius = bigCircleOuterRadius/2
val circleCenterX:Float = w.toFloat()/2 //计算圆心位置
val circleCenterY:Float = h.toFloat()/2
val globalRegion: Region = Region(0, 0, canvasWidth, canvasHeight)
var startAngel: Float = -90 - sweepAngel/2 // 开始角度,最开始画的为最上面一个,index为0,顺时针一次递增
for(i in 0 until buttonNum){
val path: Path = Path()
val region: Region = Region()
path.addArc(circleCenterX - bigCircleOuterRadius, circleCenterY - bigCircleOuterRadius, circleCenterX + bigCircleOuterRadius, circleCenterY + bigCircleOuterRadius, startAngel, sweepAngel)
path.arcTo(circleCenterX - bigCircleInnerRadius, circleCenterY - bigCircleInnerRadius, circleCenterX + bigCircleInnerRadius, circleCenterY + bigCircleInnerRadius, startAngel+sweepAngel, -sweepAngel, false)
path.close()
buttonPathList.add(path)
region.setPath(path, globalRegion)
buttonRegionList.add(region)
startAngel += sweepAngel + offsetAngel;
}
val centerPath: Path = Path() // 最后中心 ok 按键
val centerRegion: Region = Region()
centerPath.addCircle(circleCenterX, circleCenterY, enterCircleRadius, Path.Direction.CW)
centerRegion.setPath(centerPath, globalRegion)
buttonPathList.add(centerPath)
buttonRegionList.add(centerRegion)
}
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
buttonPathList.forEachIndexed { index, path ->
if (index == touchArea){
paint.color = selectedColor
}else{
paint.color = unselectedColor
}
canvas?.drawPath(path, paint)
}
}
override fun onTouchEvent(event: MotionEvent?): Boolean {
buttonRegionList.also {
it.forEachIndexed regionLoop@ { index, region ->
if (region.contains(event?.x?.toInt() ?: 0, event?.y?.toInt() ?: 0)) {
touchArea = index
if (event?.action == MotionEvent.ACTION_UP){
Log.d("zwt", "is upup:::"+index)
myOnClickListener?.onClick(index)
if(touchArea != -1){ //手松开的区域在按钮之中
previousTouchArea = -1
touchArea = -1
invalidate()
}
}
return@also
}else {
touchArea = -1
}
}
}
if (previousTouchArea != touchArea) { //重新绘制,手指按住按钮移出按钮区域的情况
previousTouchArea = touchArea
invalidate()
}
return true
}
fun setOnClickListener(myOnClickListener: MyOnClickListener) {
this.myOnClickListener = myOnClickListener
}
interface MyOnClickListener{
fun onClick(touchPosition: Int)
}
}
Java
public class JRemoteControlMenu extends View {
private Paint paint;
private final int buttonNum = 4;
private final float offsetAngel = 10f;
private final float sweepAngel = (360 - offsetAngel * buttonNum) / buttonNum; // 计算每个按键的角度
private float bigCircleOuterRadius = 300f; // 上下左右按键外半径,300f默认值
private float bigCircleInnerRadius = 200f; // 上下左右按键内半径,200f默认值
private float enterCircleRadius = 150f; // ok按键圆半径,150f默认值
private final int unselectedColor = Color.RED; // 未点击时的按键颜色
private final int selectedColor = Color.RED; // 点击时的按键颜色
private ArrayList<Region> buttonRegionList;
private ArrayList<Path> buttonPathList;
private int previousTouchArea = -1; //上一个触碰的区域
private int touchArea = -1; //触碰的区域
private MyOnClickListener myOnClickListener = null; //回调方法
public JRemoteControlMenu(Context context) {
this(context, null);
}
public JRemoteControlMenu(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public JRemoteControlMenu(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
paint = new Paint();
paint.setColor(unselectedColor);
paint.setStyle(Paint.Style.FILL);
paint.setStrokeWidth(3f);
buttonPathList = new ArrayList<Path>(buttonNum+1);
buttonRegionList = new ArrayList<Region>(buttonNum+1);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
float canvasWidth = (float) w;
float canvasHeight = (float) h;
bigCircleOuterRadius = Math.min(canvasWidth, canvasHeight)/2; // 计算圆的半径
bigCircleInnerRadius = bigCircleOuterRadius*2/3;
enterCircleRadius = bigCircleOuterRadius/2;
float circleCenterX = canvasWidth/2; //计算圆心位置
float circleCenterY = canvasHeight/2;
Region globalRegion = new Region(0, 0, w, h);
float startAngel = -90 - sweepAngel/2;
for (int i=0; i<buttonNum; i++){
Path path = new Path();
Region region = new Region();
path.addArc(circleCenterX - bigCircleOuterRadius, circleCenterY - bigCircleOuterRadius, circleCenterX + bigCircleOuterRadius, circleCenterY + bigCircleOuterRadius, startAngel, sweepAngel);
path.arcTo(circleCenterX - bigCircleInnerRadius, circleCenterY - bigCircleInnerRadius, circleCenterX + bigCircleInnerRadius, circleCenterY + bigCircleInnerRadius, startAngel+sweepAngel, -sweepAngel, false);
path.close();
buttonPathList.add(path);
region.setPath(path, globalRegion);
buttonRegionList.add(region);
startAngel += sweepAngel + offsetAngel;
}
Path centerPath = new Path(); // 最后中心 ok 按键
Region centerRegion = new Region();
centerPath.addCircle(circleCenterX, circleCenterY, enterCircleRadius, Path.Direction.CW);
centerRegion.setPath(centerPath, globalRegion);
buttonPathList.add(centerPath);
buttonRegionList.add(centerRegion);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (canvas!=null){
for (int index=0; index<buttonPathList.size(); index++){
if (index == touchArea){
paint.setColor(selectedColor);
}else{
paint.setColor(unselectedColor);
}
canvas.drawPath(buttonPathList.get(index), paint);
}
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
for (int index=0; index<buttonRegionList.size(); index++){
if (buttonRegionList.get(index).contains((int)event.getX(), (int)event.getY())){
touchArea = index;
if (event.getAction() == MotionEvent.ACTION_UP){
if (myOnClickListener!=null)
myOnClickListener.onClick(index);
if (touchArea != -1){
previousTouchArea = -1;
touchArea = -1;
invalidate();
}
break;
}else {
touchArea = -1;
}
}
if (previousTouchArea != touchArea) { //重新绘制
previousTouchArea = touchArea;
invalidate();
}
}
return true;
}
public void setOnClickListener(MyOnClickListener myOnClickListener){
this.myOnClickListener = myOnClickListener;
}
interface MyOnClickListener{
public void onClick(int touchPosition);
}
}
红外遥控工具类
class ConsumerIrManagerApi {
private var context: Context? = null
private var service: ConsumerIrManager? = null
companion object{ @SuppressLint("StaticFieldLeak")
val instance = ConsumerIrManagerApiHolder.holder }
fun getInstance(): ConsumerIrManagerApi{
return instance;
}
fun init(context: Context): Boolean{
this.context = context
// android 4.4 版本以上
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){
service = context.applicationContext.getSystemService(Context.CONSUMER_IR_SERVICE) as ConsumerIrManager?
return hasIrEmitter()
}
return false
}
// 手机是否有红外功能
fun hasIrEmitter(): Boolean{
return service?.hasIrEmitter() == true
}
// 发射红外信号
fun transmit(carrierFrequency: Int, pattern:IntArray){
Log.d("zwt",""+(service == null))
service?.transmit(carrierFrequency, pattern)
}
fun transmit(carrierFrequency: Int, customerID: String, keyCode: String ){
val paramString: String = transformationCustomerID(customerID) + transformationKeyCode(keyCode)
var pattern: IntArray = IntArray(paramString.length * 2 + 4)
pattern[0] = 9000;
pattern[1] = 4500;
var j: Int = 2
paramString.forEach{ c ->
if (c == '1'){
pattern[j] = 560
pattern[(j + 1)] = 1680
}else{
pattern[j] = 560
pattern[(j + 1)] = 560
}
j += 2
}
pattern[j] = 560
pattern[(j + 1)] = 20000
transmit(carrierFrequency, pattern)
}
// 获取可支持的红外信号
fun getCarrierFrequencies(): Array<out ConsumerIrManager.CarrierFrequencyRange>? {
return service?.carrierFrequencies
}
private object ConsumerIrManagerApiHolder {
@SuppressLint("StaticFieldLeak")
val holder = ConsumerIrManagerApi()
}
/*****************************************/
// 转换用户码
private fun transformationCustomerID(customerID: String): String{
if (customerID.length == 4){
val a = customerID.subSequence(0, 2)
val b = customerID.subSequence(2, 4)
//手机与遥控器的信号编码有区别,需要逆序编码
var sb: StringBuilder = StringBuilder(hexToBinary("$b$a", true)).reverse()
Log.d("zwt", "::::::${sb.toString()}")
return sb.toString()
}
return ""
}
private fun transformationKeyCode(keyCode: String): String{
if (keyCode.length == 2 && isHexNumber(keyCode)){
val complement: String = ("FF".toInt(16) - keyCode.toInt(16)).toString(16) //计算补码
//手机与遥控器的信号编码有区别,需要逆序编码
var sb: StringBuilder = StringBuilder(hexToBinary("$complement$keyCode", true)).reverse()
Log.d("zwt", "::::::${sb.toString()}")
return sb.toString()
}
return ""
}
// 判断字符串是否符合16进制格式
private fun isHexNumber(paramString: String): Boolean {
return paramString.matches("^[A-Fa-f0-9]+$".toRegex())
}
// 将16进制字符串转化为二进制字符串
private fun hexToBinary(paramString: String): String{
if (isHexNumber(paramString)){
// var dec: Int = paramString.toInt(16) //16进制字符串转换成10进制Int
// var binary: String = dec.toString(2) //10进制Int转换2进制字符串
return paramString.toInt(16).toString(2)
}
return ""
}
private fun hexToBinary(paramString: String, isFormat: Boolean): String{
val binaryString: String = hexToBinary(paramString)
if (binaryString == "") return ""
if (isFormat){
val binaryLength: Int = paramString.length*4
return binaryString.padStart(binaryLength, '0') // 格式化
}
return binaryString
}
}
Java
public class JConsumerIrManagerApi {
private Context context;
private ConsumerIrManager service;
private JConsumerIrManagerApi instance;
private JConsumerIrManagerApi (){
instance = ConsumerIrManagerApiHolder.holder;
}
public JConsumerIrManagerApi getInstance(){
return instance;
}
public boolean init(Context context){
this.context = context;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){
service = (ConsumerIrManager) context.getApplicationContext().getSystemService(Context.CONSUMER_IR_SERVICE);
return hasIrEmitter();
}
return false;
}
// 手机是否有红外功能
public Boolean hasIrEmitter(){
return service.hasIrEmitter() == true;
}
// 发射红外信号
public void transmit(int carrierFrequency, int[] pattern){
Log.d("zwt",""+(service == null));
service.transmit(carrierFrequency, pattern);
}
// 获取可支持的红外信号
public ConsumerIrManager.CarrierFrequencyRange[] getCarrierFrequencies() {
return service.getCarrierFrequencies();
}
public void transmit(int carrierFrequency, String customerID, String keyCode){
String paramString = transformationCustomerID(customerID) + transformationKeyCode(keyCode);
int[] pattern = new int[paramString.length() * 2 + 4];
pattern[0] = 9000;
pattern[1] = 4500;
int j = 2;
for (char c : paramString.toCharArray()){
if (c == '1'){
pattern[j] = 560;
pattern[(j + 1)] = 1680;
}else{
pattern[j] = 560;
pattern[(j + 1)] = 560;
}
j += 2;
}
pattern[j] = 560;
pattern[(j + 1)] = 20000;
transmit(carrierFrequency, pattern);
}
private static class ConsumerIrManagerApiHolder {
public static JConsumerIrManagerApi holder = new JConsumerIrManagerApi ();
}
/*****************************************/
// 转换用户码
private String transformationCustomerID(String customerID){
if (customerID.length() == 4){
String a = customerID.substring(0, 2);
String b = customerID.substring(2, 4);
//手机与遥控器的信号编码有区别,需要逆序编码
StringBuilder sb = new StringBuilder(hexToBinary(b+a, true)).reverse();
Log.d("zwt", "::::::${sb.toString()}");
return sb.toString();
}
return "";
}
private String transformationKeyCode(String keyCode) {
if (keyCode.length() == 2 && isHexNumber(keyCode)){
String complement = Integer.toHexString(Integer.parseInt("FF", 16) - Integer.parseInt(keyCode, 16)); //计算补码
//手机与遥控器的信号编码有区别,需要逆序编码
StringBuilder sb = new StringBuilder(hexToBinary(complement+keyCode, true)).reverse();
Log.d("zwt", "::::::${sb.toString()}");
return sb.toString();
}
return "";
}
// 判断字符串是否符合16进制格式
private Boolean isHexNumber(String paramString) {
return paramString.matches("^[A-Fa-f0-9]+$");
}
// 将16进制字符串转化为二进制字符串
private String hexToBinary(String paramString){
if (isHexNumber(paramString)){
return Integer.toBinaryString(Integer.parseInt(paramString, 16));
}
return "";
}
private String hexToBinary(String paramString , Boolean isFormat) {
String binaryString = hexToBinary(paramString);
if ("".equals(binaryString)) return "";
if (isFormat){
int binaryLength = paramString.length()*4;
return String.format("%"+binaryLength+"s", new Object[] {binaryString}).replace(' ', '0');
}
return binaryString;
}
}
调用
class MainActivity : Activity() {
private val TAG: String = "MainActivity"
private val customerID: String = "03fc" // 客户头码
lateinit var remoteControlMenu: RemoteControlMenu
@SuppressLint("MissingPermission")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initAllDatum()
initAllViews()
}
private fun initAllDatum(){
if (!ConsumerIrManagerApi.instance.init(this)){ // 初始化红外工具类
//TODO 手机不支持红外,初始化失败
}
}
private fun initAllViews(){
remoteControlMenu = findViewById(R.id.view111)
remoteControlMenu.setOnClickListener(object : RemoteControlMenu.MyOnClickListener{ // 监听
override fun onClick(touchPosition: Int) {
when(touchPosition){
0 -> { // 上
ConsumerIrManagerApi.instance.transmit(38000, customerID, "0b")
}
1 -> { // 右
ConsumerIrManagerApi.instance.transmit(38000, customerID, "11")
}
2 ->{ // 下
ConsumerIrManagerApi.instance.transmit(38000, customerID, "0e")
}
3 ->{ // 左
ConsumerIrManagerApi.instance.transmit(38000, customerID, "10")
}
4 ->{ // ok
ConsumerIrManagerApi.instance.transmit(38000, customerID, "0d")
}
}
}
})
}
}
//var patterns: IntArray = intArrayOf(9000,4500,// 开头两个数字表示引导码
// // 下面两行表示用户码 03fc 00000011 11111100
// 560,1680, 560,1680, 560,560, 560,560, 560,560, 560,560, 560,560, 560,560,
// 560,560, 560,560, 560,1680, 560,1680, 560,1680, 560,1680, 560,1680, 560,1680,
// // 下面一行表示数据码 0d 00001101
// 560,1680, 560,560, 560,1680, 560,1680, 560,560, 560,560, 560,560, 560,560,
// // 下面一行表示数据反码 ff-0d=f2 11110010
// 560,560, 560,1680, 560,560, 560,560, 560,1680, 560,1680, 560,1680, 560,1680,
// 560,20000)// 末尾两个数字表示结束码
稍加修饰