Android自定義控件實(shí)現(xiàn)方向盤效果
在很多開(kāi)發(fā)中,為了界面更加的友好,在自定義View的基礎(chǔ)上,開(kāi)發(fā)者會(huì)開(kāi)發(fā)出各種各樣的自定義控件來(lái)滿足實(shí)際開(kāi)發(fā)需要,其中有一種”方向盤”的控件在實(shí)際開(kāi)發(fā)中非常常見(jiàn),便于用戶進(jìn)行一些實(shí)際性的方向控制。
在復(fù)習(xí)參考了許多自定義控件的基礎(chǔ)上,我實(shí)現(xiàn)了一個(gè)最最基本的方向盤空間,并且可以根據(jù)方向做出相應(yīng)的反應(yīng)。話不多說(shuō),先看看效果。
做的有點(diǎn)丑,大家可以看看實(shí)際原理,后期再優(yōu)化具體“方向盤”.

空間下面的幾行字是我為了確定方向所寫的一些參數(shù),基本思想就是在方向盤的中心確定一個(gè)坐標(biāo)軸,根據(jù)中間這個(gè)小圓的和中心點(diǎn)的距離與方向確定所處的方向。在手離開(kāi)屏幕以后,小圓回到原點(diǎn)。
一言不合就放代碼~~~~
具體是怎么實(shí)現(xiàn)的呢??
來(lái)我們一起看看代碼,看完一目了然。
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
}
//先定義一些繪圖要用的基本參數(shù)
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; //整個(gè)控件的大圓,及小紅點(diǎn)的活動(dòng)范圍
//自定義的接口用于監(jiān)聽(tīng)處理控件的觸摸事件
private OnMyWheelMoveListener onmywheelMoveListener;
private Paint secondaryCircle;//第二個(gè)內(nèi)圓,小紅圓超過(guò)后開(kāi)始處理角度
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這個(gè)名字有點(diǎn)太隨便了........
}
public MyWheel(Context paramContext, AttributeSet paramAttributeSet,
int paramInt) {
super(paramContext, paramAttributeSet, paramInt);
initMyWheel();
}
//根據(jù)所處的位置得到角度
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;
}
//根據(jù)紅色圓的距離和角度得到方向
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;
}
//初始化一些基本參數(shù)
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) {
//根據(jù)手觸碰的坐標(biāo)決定紅色小圓的位置
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, "運(yùn)行在"+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) {
/*處理這個(gè)控件的觸摸事件*/
return true;
}
}
怎么用?下面我給出我的調(diào)用實(shí)例進(jìn)行講解
首先在XML文件中應(yīng)用。
<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="準(zhǔn)備" />
<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="零態(tài)" />
</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="這是簡(jiǎn)單控制界面" />
<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>
在一個(gè)Fragment中引用實(shí)例并處理相應(yīng)監(jiān)聽(tīng)事件。
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);
//下面的監(jiān)聽(tīng)器代碼最為重要!?。。。。。?!
mtwheel.setOnMyWheelMoveListener(new OnMyWheelMoveListener() {
@Override
// paramInt1:角度
// paramInt2:距離 根據(jù)這兩個(gè)參數(shù)可以算出方向盤的方位
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" + "左轉(zhuǎn)", 0)
.show();
if (!isturn) {
Log.e(tag, "onProgressChanged" + "左轉(zhuǎn)");
command = "turnleft";
udpUtil.UdpSend(command, Constant.port);
vibator.vibrate(100);
isturn = true;
}
} else if (cucrrent > 80) {
Toast.makeText(getActivity(), "onProgressChanged" + "右轉(zhuǎn)", 0)
.show();
if (!isturn) {
Log.e(tag, "onProgressChanged" + "右轉(zhuǎn)");
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.上面的控件代碼(第一部分代碼)可以實(shí)際使用
2.第二部分代碼演示了控件的使用與處理
3.關(guān)于控件的實(shí)現(xiàn)原理和思想在代碼與注釋中已經(jīng)詳細(xì)標(biāo)記
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Android自定義控件實(shí)現(xiàn)折線圖
- 詳解Android圖表 MPAndroidChart折線圖
- MPAndroidChart開(kāi)源圖表庫(kù)的使用介紹之餅狀圖、折線圖和柱狀圖
- Android自定義View實(shí)現(xiàn)折線圖效果
- Android開(kāi)發(fā)之天氣趨勢(shì)折線圖
- Android HelloChart開(kāi)源庫(kù)圖表之折線圖的實(shí)例代碼
- Android繪制動(dòng)態(tài)折線圖
- Android自定義可左右滑動(dòng)和點(diǎn)擊的折線圖
- Android自定義控件實(shí)現(xiàn)圓形進(jìn)度CircleProgressBar
- Android自定義控件實(shí)現(xiàn)時(shí)鐘效果
- Android開(kāi)發(fā)自定義控件之折線圖實(shí)現(xiàn)方法詳解
相關(guān)文章
Android開(kāi)發(fā)之資源目錄assets與res/raw的區(qū)別分析
這篇文章主要介紹了Android開(kāi)發(fā)之資源目錄assets與res/raw的區(qū)別,結(jié)合實(shí)例形式分析了Android開(kāi)發(fā)中資源目錄assets與res/raw的具體功能、使用方法與區(qū)別,需要的朋友可以參考下2016-01-01
Android開(kāi)發(fā)中關(guān)于獲取當(dāng)前Activity的一些思考
這篇文章主要為大家詳細(xì)介紹了Android開(kāi)發(fā)過(guò)程中,關(guān)于獲取當(dāng)前Activity的一些思考,感興趣的小伙伴們可以參考一下2016-02-02
Android自定義有限制區(qū)域圖例角度自識(shí)別涂鴉工具類
這篇文章主要為大家介紹了Android自定義有限制區(qū)域圖例角度自識(shí)別涂鴉工具類,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02
Android Studio中快捷鍵實(shí)現(xiàn)try catch等功能包含代碼塊的實(shí)現(xiàn)方法
這篇文章主要介紹了 Android Studio中快捷鍵實(shí)現(xiàn)try catch等功能包含代碼塊的實(shí)現(xiàn)方法的相關(guān)資料,希望通過(guò)本文能幫助到大家,需要的朋友可以參考下2017-09-09
Android入門之ListView應(yīng)用解析(一)
這篇文章主要介紹了Android入門之ListView應(yīng)用,簡(jiǎn)單說(shuō)明了ListView的實(shí)現(xiàn),需要的朋友可以參考下2014-08-08
android實(shí)現(xiàn)一個(gè)圖片驗(yàn)證碼倒計(jì)時(shí)功能
本文通過(guò)實(shí)例代碼給大家介紹了android實(shí)現(xiàn)一個(gè)圖片驗(yàn)證碼倒計(jì)時(shí)功能,代碼簡(jiǎn)單易懂,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友參考下吧2017-11-11
Android 文件存儲(chǔ)及常見(jiàn)問(wèn)題解決
這篇文章主要介紹了Android 文件存儲(chǔ)及常見(jiàn)問(wèn)題解決的相關(guān)資料,需要的朋友可以參考下2017-02-02
android設(shè)備間實(shí)現(xiàn)無(wú)線投屏的示例代碼
Android提供了MediaProjection來(lái)實(shí)現(xiàn)錄屏,通過(guò)MediaProjection可以獲取當(dāng)前屏幕的視頻流,而視頻流需要通過(guò)編解碼來(lái)壓縮進(jìn)行傳輸,通過(guò)MediaCodec可實(shí)現(xiàn)視頻的編碼和解碼,這篇文章主要介紹了android設(shè)備間實(shí)現(xiàn)無(wú)線投屏,需要的朋友可以參考下2022-06-06

