效果图
1.找两张图片
1)Joystick背景图片circle_1.png
2)Joystick图片circle_2.png
2.在layout中创建布局文件 imagejoystick.xml
内容为:
<?xml version="1.0"encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/ivBackground"
android:layout_gravity="center"
android:src="@drawable/circle_1"/>
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
android:id="@+id/ivJoystick"
android:src="@drawable/circle_2"
android:visibility="visible"
/>
</FrameLayout>
效果如图:
3.在value文件夹中添加sttrs.xml文件,添加控件属性,内容为
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="JoystickView">//可以是自定义控件名也可以是其它的
<attr name="backgroundDiameter" format="dimension"/>
<attr name="joystickDiameter" format="dimension"/>
</declare-styleable>
</resources>
format: reference 资源ID
color 颜色
boolean 布尔变量
dimension 尺寸值
float 浮点值
integer 整形值
string 字符串
fraction 百分比
enum枚举值
flag 位或运算
4.在工程目录下添加自定义控件文件JoystickView
//JoystickView
package com.innovpower.uav.CustomView;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import com.innovpower.uav.R;
/**
* Created by ${chenxi} on 2015/12/8.
*/
public class JoystickView extends LinearLayout {
private View inflate;
private int backgroundDiameter;
private int joystickDiameter;
private FrameLayout.LayoutParams layoutParams;
private ImageView ivJoystick;
private ImageView ivBackground;
private JoyStickListener listener = null; // 事件回调接口
public JoystickView(Context context) {
super(context);
inflate = LayoutInflater.from(context).inflate(R.layout.imagejoystick, this, true);
ivJoystick = (ImageView) inflate.findViewById(R.id.ivJoystick);
ivBackground = (ImageView) inflate.findViewById(R.id.ivBackground);
}
public JoystickView(Context context, AttributeSet attrs) {
super(context, attrs);
inflate = LayoutInflater.from(context).inflate(R.layout.imagejoystick, this, true);
ivJoystick = (ImageView) inflate.findViewById(R.id.ivJoystick);
ivBackground = (ImageView) inflate.findViewById(R.id.ivBackground);
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.JoystickView); //与属性名称一致
backgroundDiameter = (int) array.getDimension(R.styleable.JoystickView_backgroundDiameter, 40);//第一个是传递参数,第二个是默认值
joystickDiameter = (int) array.getDimension(R.styleable.JoystickView_joystickDiameter, 40);
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
initBackground(0, 0);
layoutParams = (FrameLayout.LayoutParams) ivJoystick
.getLayoutParams();
layoutParams.height = joystickDiameter;
layoutParams.width = joystickDiameter;
ivJoystick.setLayoutParams(layoutParams);
ivJoystick.setVisibility(INVISIBLE);
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int xOrigin = getWidth() / 2 - joystickDiameter / 2;
int yOrigin = getHeight() / 2 - joystickDiameter / 2;
int x_touch = (int) event.getX() - joystickDiameter / 2;
int y_touch = (int) event.getY() - joystickDiameter / 2;
int limit = backgroundDiameter / 2 - joystickDiameter / 2;
if (event.getAction() == MotionEvent.ACTION_DOWN || event.getAction() == MotionEvent.ACTION_MOVE) {
ivJoystick.setVisibility(VISIBLE);
//得到摇杆与触屏点所形成的角度
double tempRad = getRad(xOrigin, yOrigin, x_touch, y_touch);
if (Math.sqrt(Math.pow((xOrigin - x_touch), 2) + Math.pow((yOrigin - y_touch), 2)) >= limit) {
//保证内部小圆运动的长度限制
getXY(xOrigin, yOrigin, limit, tempRad);
} else {//如果小球中心点小于活动区域则随着用户触屏点移动即可
Stickmove(x_touch, y_touch);
}
if (listener!=null)
listener.onSteeringWheelChanged(radToAngle(tempRad));
} else if (event.getAction() == MotionEvent.ACTION_UP) {
//当释放按键时摇杆要恢复摇杆的位置为初始位置
Stickmove(xOrigin, yOrigin);
ivJoystick.setVisibility(INVISIBLE);
}
return true;
}
private double radToAngle(double rad){
return (180*rad)/Math.PI;
}
private void initBackground(int x, int y) {//将背景圆移动到中心
FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) ivBackground
.getLayoutParams();
layoutParams.leftMargin = x;
layoutParams.topMargin = y;
layoutParams.width = backgroundDiameter;
layoutParams.height = backgroundDiameter;
ivBackground.setLayoutParams(layoutParams);
}
private void Stickmove(int x, int y) {
layoutParams.leftMargin = x;
layoutParams.topMargin = y;
ivJoystick.setLayoutParams(layoutParams);
}
/***
* 得到两点之间的弧度
*/
private double getRad(float px1, float py1, float px2, float py2) {
//得到两点X的距离
float x = px2 - px1;
//得到两点Y的距离
float y = py1 - py2;
//算出斜边长
float xie = (float) Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
//得到这个角度的余弦值(通过三角函数中的定理 :邻边/斜边=角度余弦值)
float cosAngle = x / xie;
//通过反余弦定理获取到其角度的弧度
float rad = (float) Math.acos(cosAngle);
//注意:当触屏的位置Y坐标<摇杆的Y坐标我们要取反值-0~-180
if (py2 < py1) {
rad = -rad;
}
return rad;
}
//函数:getXY
//功能:限制joystick移动范围不得大于R
//参数:centerX,centerY圆心的x,y
// rad触点与圆心形成的直线与水平线之间的夹角
private void getXY(float centerX, float centerY, float R, double rad) {
int x, y;
//获取圆周运动的X坐标
x = (int) ((float) (R * Math.cos(rad)) + centerX);
//获取圆周运动的Y坐标
y = (int) ((float) (R * Math.sin(rad)) + centerY);
Stickmove(x, y);
/* if (listener!=null)
listener.onSteeringWheelChanged(x,y);*/
}
// 设置回调接口
public void setJoystickListener(JoyStickListener rockerListener)
{
listener = rockerListener;
}
// 回调接口
public interface JoyStickListener
{
void onSteeringWheelChanged(double angle);
}
}
5.在布局中调用控件
<RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:my_attrs="http://schemas.android.com/apk/res-auto"
(eclipse/IDEA : xmlns:custom="http://schemas.android.com/apk/res/com.XXX")
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context=".MainActivity">
<TextViewandroid:text="@string/hello_world"android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<com.demo.chenxi.joystickdemo.CustomView.JoystickView
android:layout_width="300dp"
android:layout_height="300dp"
my_attrs:backgroundDiameter="200dp"
my_attrs:joystickDiameter="100dp"
>
</com.demo.chenxi.joystickdemo.CustomView.JoystickView>
</RelativeLayout>
6.在mainacitivity中调用控件事件
publicclass MainActivity extends Activity {
private JoystickView joystick;
@Override
protected void onCreate(BundlesavedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
joystick=(JoystickView)findViewById(R.id.myJoystick);
joystick.setJoystickListener(newJoystickView.JoyStickListener() {
@Override
public voidonSteeringWheelChanged(int angle) {
Log.i("chenxi","angle:" + angle);
}
});
}
工程下载地址: