Android自定义控件实现方向盘效果

来自:网络
时间:2020-10-14
阅读:

在很多开发中,为了界面更加的友好,在自定义View的基础上,开发者会开发出各种各样的自定义控件来满足实际开发需要,其中有一种”方向盘”的控件在实际开发中非常常见,便于用户进行一些实际性的方向控制。

在复习参考了许多自定义控件的基础上,我实现了一个最最基本的方向盘空间,并且可以根据方向做出相应的反应。话不多说,先看看效果。

做的有点丑,大家可以看看实际原理,后期再优化具体“方向盘”.

Android自定义控件实现方向盘效果

空间下面的几行字是我为了确定方向所写的一些参数,基本思想就是在方向盘的中心确定一个坐标轴,根据中间这个小圆的和中心点的距离与方向确定所处的方向。在手离开屏幕以后,小圆回到原点。

一言不合就放代码~~~~

具体是怎么实现的呢??

来我们一起看看代码,看完一目了然。

package com.sshhsun.socketudp.utils;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

public class MyWheel extends View implements Runnable,View.OnTouchListener {

  public MyWheel(Context context) {
    super(context);
    // TODO Auto-generated constructor stub
  }

  //先定义一些绘图要用的基本参数
  public static final int BOTTOM = 7;
  public static final int BOTTOM_LEFT = 8;
  public static final long DEFAULT_LOOP_INTERVAL = 100L;
  public static final int FRONT = 3;
  public static final int FRONT_RIGHT = 4;
  public static final int LEFT = 1;
  public static final int LEFT_FRONT = 2;
  public static final int RIGHT = 5;
  public static final int RIGHT_BOTTOM = 6;
  private final double RAD = 57.295779500000002D;
  private Paint button;
  private int buttonRadius;
  public double centerX = 0.0D;
  public double centerY = 0.0D;
  private Paint horizontalLine;
  private int joystickRadius;
  private int lastAngle = 0;
  private int lastPower = 0;
  private long loopInterval = 100L;
  private Paint mainCircle;  //整个控件的大圆,及小红点的活动范围


  //自定义的接口用于监听处理控件的触摸事件
  private OnMyWheelMoveListener onmywheelMoveListener;
  private Paint secondaryCircle;//第二个内圆,小红圆超过后开始处理角度
  private Thread thread = new Thread(this);
  private Paint verticalLine;
  private int xPosition = 0;
  private int yPosition = 0;
  private static final String tag="MyWheel";

  public MyWheel(Context paramContext, AttributeSet paramAttributeSet) {
    super(paramContext, paramAttributeSet);
    initMyWheel();  //好吧,我知道MyWheel这个名字有点太随便了........
  }

  public MyWheel(Context paramContext, AttributeSet paramAttributeSet,
      int paramInt) {
    super(paramContext, paramAttributeSet, paramInt);
    initMyWheel();
  }

  //根据所处的位置得到角度
  private int getAngle() {
    if (this.xPosition > this.centerX) {
      if (this.yPosition < this.centerY) {
        int m = (int) (90.0D + 57.295779500000002D * Math
            .atan((this.yPosition - this.centerY)
                / (this.xPosition - this.centerX)));
        this.lastAngle = m;
        return m;
      }
      if (this.yPosition > this.centerY) {
        int k = 90 + (int) (57.295779500000002D * Math
            .atan((this.yPosition - this.centerY)
                / (this.xPosition - this.centerX)));
        this.lastAngle = k;
        return k;
      }
      this.lastAngle = 90;
      return 90;
    }
    if (this.xPosition < this.centerX) {
      if (this.yPosition < this.centerY) {
        int j = (int) (57.295779500000002D * Math
            .atan((this.yPosition - this.centerY)
                / (this.xPosition - this.centerX)) - 90.0D);
        this.lastAngle = j;
        return j;
      }
      if (this.yPosition > this.centerY) {
        int i = -90
            + (int) (57.295779500000002D * Math
                .atan((this.yPosition - this.centerY)
                    / (this.xPosition - this.centerX)));
        this.lastAngle = i;
        return i;
      }
      this.lastAngle = -90;
      return -90;
    }
    if (this.yPosition <= this.centerY) {
      this.lastAngle = 0;
      return 0;
    }
    if (this.lastAngle < 0) {
      this.lastAngle = -180;
      return -180;
    }
    this.lastAngle = 180;
    return 180;
  }

  //根据红色圆的距离和角度得到方向
  private int getDirection() {
    int k;
    int j = 0;
    int i;
    if ((this.lastPower == 0) && (this.lastAngle == 0)) {
      k = 0;
      return k;
    }

    if (this.lastAngle <= 0)
      j = 90 + -1 * this.lastAngle;
    while (true) {
      k = 1 + (j + 22) / 45;
      if (k <= 8) {
        break;
      }

      if (this.lastAngle <= 90) {
        j = 90 - this.lastAngle;
        continue;
      }
      j = 360 - (-90 + this.lastAngle);
    }
    return k;
  }

  //得到红色圆与中心的距离
  private int getPower() {
    return (this.lastPower=(int) (100.0D * Math.sqrt((this.xPosition - this.centerX)
        * (this.xPosition - this.centerX)
        + (this.yPosition - this.centerY)
        * (this.yPosition - this.centerY)) / this.joystickRadius));
  }

  private int measure(int paramInt) {
    int i = View.MeasureSpec.getMode(paramInt);
    int j = View.MeasureSpec.getSize(paramInt);
    if (i == 0)
      return 200;
    return j;
  }


  //初始化一些基本参数
  protected void initMyWheel() {
    this.mainCircle = new Paint(1);
    this.mainCircle.setColor(Color.BLUE);
    this.mainCircle.setStrokeWidth(3.0f);
    this.mainCircle.setStyle(Paint.Style.STROKE);
    this.secondaryCircle = new Paint();
    this.secondaryCircle.setColor(-16711936);
    this.secondaryCircle.setStrokeWidth(3.0f);
    this.secondaryCircle.setStyle(Paint.Style.STROKE);
    this.verticalLine = new Paint();
    this.verticalLine.setStrokeWidth(5.0F);
    this.verticalLine.setColor(-65536);
    this.horizontalLine = new Paint();
    this.horizontalLine.setStrokeWidth(2.0F);
    this.horizontalLine.setColor(-16777216);
    this.button = new Paint(1);
    this.button.setColor(Color.RED);
    this.button.setStyle(Paint.Style.FILL);
  }

  //初始化以后绘制方向盘。
  protected void onDraw(Canvas paramCanvas) {
    this.centerX = (getWidth() / 2);
    this.centerY = (getHeight() / 2);
    paramCanvas.drawCircle((int) this.centerX, (int) this.centerY,
        this.joystickRadius, this.mainCircle);
    paramCanvas.drawCircle((int) this.centerX, (int) this.centerY,
        this.joystickRadius / 2, this.secondaryCircle);
    paramCanvas
        .drawLine((float) this.centerX, (float) this.centerY,
            (float) this.centerX,
            (float) (this.centerY - this.joystickRadius),
            this.verticalLine);
    paramCanvas.drawLine((float) (this.centerX - this.joystickRadius),
        (float) this.centerY,
        (float) (this.centerX + this.joystickRadius),
        (float) this.centerY, this.horizontalLine);
    paramCanvas
        .drawLine((float) this.centerX,
            (float) (this.centerY + this.joystickRadius),
            (float) this.centerX, (float) this.centerY,
            this.horizontalLine);
    paramCanvas.drawCircle(this.xPosition, this.yPosition,
        this.buttonRadius, this.button);
  }

  protected void onFinishInflate() {
  }

  protected void onMeasure(int paramInt1, int paramInt2) {
    int i = Math.min(measure(paramInt1), measure(paramInt2));
    setMeasuredDimension(i, i);
  }

  protected void onSizeChanged(int paramInt1, int paramInt2, int paramInt3,
      int paramInt4) {
    super.onSizeChanged(paramInt1, paramInt2, paramInt3, paramInt4);
    this.xPosition = (getWidth() / 2);
    this.yPosition = (getWidth() / 2);
    int i = Math.min(paramInt1, paramInt2);
    this.buttonRadius = (int) (0.20D * (i / 2));
    this.joystickRadius = (int) (0.75D * (i / 2));
  }

  @Override
  public boolean onTouchEvent(MotionEvent paramMotionEvent) {
    //根据手触碰的坐标决定红色小圆的位置
    this.xPosition = (int) paramMotionEvent.getX();
    this.yPosition = (int) paramMotionEvent.getY();
    double d = Math.sqrt((this.xPosition - this.centerX)
        * (this.xPosition - this.centerX)
        + (this.yPosition - this.centerY)
        * (this.yPosition - this.centerY));
    if (d > this.joystickRadius) {
      this.xPosition = (int) ((this.xPosition - this.centerX)
          * this.joystickRadius / d + this.centerX);
      this.yPosition = (int) ((this.yPosition - this.centerY)
          * this.joystickRadius / d + this.centerY);
    }
    invalidate();//再重新绘制
    if (paramMotionEvent.getAction() == 1) {
      this.xPosition = (int) this.centerX;
      this.yPosition = (int) this.centerY;
      this.thread.interrupt();
      if (this.onmywheelMoveListener != null)
        this.onmywheelMoveListener.onValueChanged(getAngle(),
            getPower());
    }
    if ((this.onmywheelMoveListener != null)
        && (paramMotionEvent.getAction() == 0)) {
      if ((this.thread != null) && (this.thread.isAlive()))
        this.thread.interrupt();
      this.thread = new Thread(this);
      this.thread.start();
      if (this.onmywheelMoveListener != null)
        //自定义接口处理触摸事件
        this.onmywheelMoveListener.onValueChanged(getAngle(),
            getPower());
    }
    return true;
  }

  @Override
  public void run() {
    while (true) {
      if (Thread.interrupted())
        return;
      post(new Runnable() {
        public void run() {
//         Log.e(tag, "运行在"+Thread.currentThread().getName()+"线程中");
          if (MyWheel.this.onmywheelMoveListener != null)
            MyWheel.this.onmywheelMoveListener.onValueChanged(
                MyWheel.this.getAngle(),
                MyWheel.this.getPower());
        }
      });
      try {
        Thread.sleep(this.loopInterval);
      } catch (InterruptedException localInterruptedException) {
      }
    }
  }
  public void setOnMyWheelMoveListener(
      OnMyWheelMoveListener paramOnJoystickMoveListener, long paramLong) {
    this.onmywheelMoveListener = paramOnJoystickMoveListener;
    this.loopInterval = paramLong;
  }

  public static abstract interface OnMyWheelMoveListener {
    public abstract void onValueChanged(int paramInt1, int paramInt2);
  }

  @SuppressLint("ClickableViewAccessibility")
  @Override
  public boolean onTouch(View v, MotionEvent event) {
    /*处理这个控件的触摸事件*/
    return true;
  }
}

怎么用?下面我给出我的调用实例进行讲解

首先在XML文件中应用。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:gravity="center"
  android:orientation="vertical" >

  <LinearLayout
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="horizontal" >

    <Button
      android:id="@+id/simple_rest"
      android:layout_width="0dp"
      android:layout_height="wrap_content"
      android:layout_weight="1"
      android:text="蹲下" />

    <Button
      android:id="@+id/simple_stand"
      android:layout_width="0dp"
      android:layout_height="wrap_content"
      android:layout_weight="1"
      android:text="站立" />

    <Button
      android:id="@+id/simple_standinit"
      android:layout_width="0dp"
      android:layout_height="wrap_content"
      android:layout_weight="1"
      android:text="准备" />

    <Button
      android:id="@+id/simple_sit"
      android:layout_width="0dp"
      android:layout_height="wrap_content"
      android:layout_weight="1"
      android:text="坐下" />

    <Button
      android:id="@+id/simple_standzero "
      android:layout_width="0dp"
      android:layout_height="wrap_content"
      android:layout_weight="1"
      android:text="零态" />
  </LinearLayout>

  <com.sshhsun.socketudp.utils.MyWheel
    android:id="@+id/mywheel"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />

  <TextView
    android:id="@+id/notice"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="这是简单控制界面" />

  <LinearLayout
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="vertical" >

    <SeekBar
      android:id="@+id/turns"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:minHeight="3dp"
      android:minWidth="260dp"
      android:progress="100" />
  </LinearLayout>

</LinearLayout>

在一个Fragment中引用实例并处理相应监听事件。

package com.sshhsun.socketudp.fragment;

import android.content.Context;
import android.os.Bundle;
import android.os.Vibrator;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.WebView.FindListener;
import android.widget.Button;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;
import android.widget.Toast;

import com.sshhsun.socketudp.R;
import com.sshhsun.socketudp.activity.constant.Constant;
import com.sshhsun.socketudp.utils.MyWheel;
import com.sshhsun.socketudp.utils.MyWheel.OnMyWheelMoveListener;
import com.sshhsun.socketudp.utils.UDPUtil;

public class SimpleFragment extends Fragment implements View.OnClickListener {

  private MyWheel mtwheel;
  private TextView notice;
  private TextView show;
  private String direction = "none";
  private SeekBar seekbar;
  private static final String tag = "SimpleFragment";
  Vibrator vibator;
  private Context context = getActivity();
  private boolean isturn = false;
  private Button stand;
  private Button sit;
  private Button standinit;
  private Button rest;
  private Button standzero;
  private UDPUtil udpUtil;
  private boolean issend = false;
  private boolean isstop = true;

  @Override
  public View onCreateView(LayoutInflater inflater, ViewGroup container,
      Bundle savedInstanceState) {
    return initView(inflater, container, savedInstanceState);
  }

  @Override
  public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    initData();
    initListener();
  }

  public View initView(LayoutInflater inflater, ViewGroup container,
      Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.frag_simple, null);

    //我的方向盘控件mtwheel
    mtwheel = (MyWheel) view.findViewById(R.id.mywheel);

    //控件下面的提示信息notice,其他控件大家可以忽略.
    notice = (TextView) view.findViewById(R.id.notice);
    seekbar = (SeekBar) view.findViewById(R.id.turns);
    seekbar.setProgress(50);
    stand = (Button) view.findViewById(R.id.simple_stand);
    sit = (Button) view.findViewById(R.id.simple_sit);
    standinit = (Button) view.findViewById(R.id.simple_standinit);
    rest = (Button) view.findViewById(R.id.simple_rest);
    standzero = (Button) view.findViewById(R.id.simple_standzero);
    return view;
  }

  public void initListener() {
    sit.setOnClickListener(this);
    standinit.setOnClickListener(this);
    rest.setOnClickListener(this);
    standzero.setOnClickListener(this);
    stand.setOnClickListener(this);


    //下面的监听器代码最为重要!!!!!!!!
    mtwheel.setOnMyWheelMoveListener(new OnMyWheelMoveListener() {

      @Override
      // paramInt1:角度
      // paramInt2:距离 根据这两个参数可以算出方向盘的方位
      public void onValueChanged(int paramInt1, int paramInt2) {
        boolean isdistance = false;
        if (paramInt2 >= 50) {
          isdistance = true;
          int temp = Math.abs(paramInt1);
          if (paramInt1 >= 0) {
            if (temp > 50 && temp < 120) {
              direction = "right";
              if (!issend) {
                udpUtil.UdpSend(direction, Constant.port);
                issend = true;
                isstop = false;
              }
            } else if (temp < 40) {
              direction = "forward";
              if (!issend) {
                udpUtil.UdpSend(direction, Constant.port);
                issend = true;
                isstop = false;
              }
            } else if (temp > 140) {
              direction = "back";
              if (!issend) {
                udpUtil.UdpSend(direction, Constant.port);
                issend = true;
                isstop = false;
              }
            } else {
              direction = "指向不明确";
              issend = false;
            }
          } else {
            if (temp > 50 && temp < 120) {
              direction = "left";
              if (!issend) {
                udpUtil.UdpSend(direction, Constant.port);
                issend = true;
                isstop = false;
              }
            } else if (temp < 40) {
              direction = "forward";
              if (!issend) {
                udpUtil.UdpSend(direction, Constant.port);
                issend = true;
                isstop = false;
              }
            } else if (temp > 140) {
              direction = "back";
              if (!issend) {
                udpUtil.UdpSend(direction, Constant.port);
                issend = true;
                isstop = false;
              }
            } else {
              direction = "指向不明确";
              issend = false;
            }
          }
        } else {
          isdistance = false;
          direction = "stop";
          issend = false;
        }
        notice.setText(" getAngle:" + paramInt1 + "\n" + " getPower:"
            + paramInt2 + "\n" + "direction:" + direction);

        if (direction.equals("stop") && (!isstop)) {
          udpUtil.UdpSend(direction, Constant.port);
          isstop = true;
        }
      }
    }, 100L);
    seekbar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {

      @Override
      public void onStopTrackingTouch(SeekBar seekBar) {
        seekbar.setProgress(50);
        isturn = false;
        String command = "stop";
        udpUtil.UdpSend(command, Constant.port);
      }

      @Override
      public void onStartTrackingTouch(SeekBar seekBar) {

      }

      @Override
      public void onProgressChanged(SeekBar seekBar, int progress,
          boolean fromUser) {
        int cucrrent = seekbar.getProgress();
        String command = "hello";
        if (cucrrent < 20) {
          Toast.makeText(getActivity(), "onProgressChanged" + "左转", 0)
              .show();
          if (!isturn) {
            Log.e(tag, "onProgressChanged" + "左转");
            command = "turnleft";
            udpUtil.UdpSend(command, Constant.port);
            vibator.vibrate(100);
            isturn = true;
          }
        } else if (cucrrent > 80) {
          Toast.makeText(getActivity(), "onProgressChanged" + "右转", 0)
              .show();
          if (!isturn) {
            Log.e(tag, "onProgressChanged" + "右转");
            command = "turnright";
            udpUtil.UdpSend(command, Constant.port);
            vibator.vibrate(100);
            isturn = true;
          }
        }
      }
    });

  }

  public void initData() {
    udpUtil = new UDPUtil(Constant.Address);
    vibator = (Vibrator) getActivity().getSystemService(
        Context.VIBRATOR_SERVICE);
    Thread.currentThread().setName(tag);
  }

  public void processClick(View v) {
    String command = "hello";
    switch (v.getId()) {
    case R.id.simple_rest:
      command = "rest";
      break;
    case R.id.simple_sit:
      command = "sit";

      break;
    case R.id.simple_stand:
      command = "stand";

      break;
    case R.id.simple_standinit:
      command = "standinit";

      break;
    case R.id.simple_standzero:
      command = "standzero";

      break;
    default:
      break;
    }
    udpUtil.UdpSend(command, Constant.port);
  }

  @Override
  public void onClick(View v) {
    processClick(v);
  }

  @Override
  public void onDestroy() {
    super.onDestroy();
    vibator.cancel();
  }
  // @Override
  // public boolean onTouch(View v, MotionEvent event) {
  // if (v.getId() == R.id.turns) {
  // String notice = "";
  // switch (event.getAction()) {
  // case MotionEvent.ACTION_DOWN:
  // notice = "ACTION_DOWN"+event.getX();
  // int process=(int) Math.floor(event.getX()+0.5);
  // seekbar.setProgress(process);
  // break;
  // case MotionEvent.ACTION_UP:
  // notice = "ACTION_UP";
  // break;
  // case MotionEvent.ACTION_CANCEL:
  // notice = "ACTION_CANCEL";
  // break;
  // default:
  // break;
  // }
  // if (!TextUtils.isEmpty(notice)) {
  // Toast.makeText(getActivity(), notice, 0).show();
  // }
  // }
  // return true;
  // }

}

声明一下:

1.上面的控件代码(第一部分代码)可以实际使用
2.第二部分代码演示了控件的使用与处理
3.关于控件的实现原理和思想在代码与注释中已经详细标记

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持免费资源网。

返回顶部
顶部