在很多开发中,为了界面更加的友好,在自定义View的基础上,开发者会开发出各种各样的自定义控件来满足实际开发需要,其中有一种”方向盘”的控件在实际开发中非常常见,便于用户进行一些实际性的方向控制。
在复习参考了许多自定义控件的基础上,我实现了一个最最基本的方向盘空间,并且可以根据方向做出相应的反应。话不多说,先看看效果。
做的有点丑,大家可以看看实际原理,后期再优化具体“方向盘”.
空间下面的几行字是我为了确定方向所写的一些参数,基本思想就是在方向盘的中心确定一个坐标轴,根据中间这个小圆的和中心点的距离与方向确定所处的方向。在手离开屏幕以后,小圆回到原点。
具体是怎么实现的呢??
来我们一起看看代码,看完一目了然。
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.关于控件的实现原理和思想在代码与注释中已经详细标记