Android自定义控件实现方向盘效果
作者:xaut-sunqi
这篇文章主要为大家详细介绍了Android自定义控件实现方向盘效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
在很多开发中,为了界面更加的友好,在自定义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.关于控件的实现原理和思想在代码与注释中已经详细标记
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。