欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

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

程序员文章站 2023-11-09 20:02:58
在很多开发中,为了界面更加的友好,在自定义view的基础上,开发者会开发出各种各样的自定义控件来满足实际开发需要,其中有一种”方向盘”的控件在实际开发中非常常见,便于用户进行一些实际性的方向控制。在复...

在很多开发中,为了界面更加的友好,在自定义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.关于控件的实现原理和思想在代码与注释中已经详细标记

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