自定义遥感控件

package com.efrobot.client.remotecontrol.customer;

import android.content.Context;
import android.graphics.Point;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import com.efrobot.client.remotecontrol.R;

/**
 * Created by guo on 2016/6/8.
 * 自定义方向控制,控制机器人的运动方向
 * 1.在onTouch事件的时候保存手指点下的位置,保存成两个point对象,一个是背景的backgroundPoint,一个是轨迹球的point。调用requestLayout重新调用onLayout进行摆放位置
 * 2.在onMove事件中
 */
public class RudderView extends ViewGroup {

    public final static int MODEL_ALL = 0;
    public final static int MODEL_UP_DOWN = 1;
    public final static int MODEL_LEFT_RIGHT = 2;

    private int viewWidth;
    private int viewHeight;

    private int model = MODEL_ALL;

    private boolean isProcessTouch;

    /**
     * 轨迹球滑动监听
     */
    private RudderViewListener mRudderViewListener;

    /**
     * 是否子控件
     */
    private boolean isOnLayout = false;

    /**
     * 背景View
     */
    private Point backGroundPoint = new Point();

    /**
     * 轨迹圆点View
     */
    private Point mPoint = new Point();

    /**
     * 轨迹圆点得活动范围
     */
    private int pointDistance = 0;

    /**
     * 是否显示子View
     */
    private boolean isShow = true;

    /**
     * xml中声明此View,生成View对象时会调用此构造函数
     * @param context 上下文
     * @param attrs view的属性
     */
    public RudderView(Context context, AttributeSet attrs) {
        super(context, attrs);
        //初始化背景和轨迹球id
        int[] ids ={R.id.tv_point, R.id.tv_bg};
        //初始化控件和轨迹球的背景图片
        int[] backGrounds ={R.mipmap.remote_control_point, R.mipmap.remote_control_background};
        LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
        //初始化控件和轨迹球
        for(int i = 0; i < ids.length; i++) {
            TextView textView = new TextView(context);
            textView.setId(ids[i]);
            textView.setLayoutParams(params);
            textView.setBackgroundResource(backGrounds[i]);
            addView(textView);
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        measureChildren(widthMeasureSpec, heightMeasureSpec);
        viewWidth = getMeasuredWidth();
        viewHeight = getMeasuredHeight();
    }

    public void enableProcessTouch(boolean isProcessTouch) {
        this.isProcessTouch = isProcessTouch;
    }


    /**
     * 分布view的位置
     * @param view  需要分布的view
     * @param mPoint 位置点
     */
    private void layoutChildView(View view, Point mPoint) {

        int x = 0;
        int y = 0;

        int measuredWidth = view.getMeasuredWidth();
        int measuredHeight = view.getMeasuredHeight();
        //计算出 绘制的
        int left = mPoint.x - measuredWidth / 2;
        int top = mPoint.y - measuredHeight / 2;
        int right = mPoint.x + measuredWidth / 2;
        int bottom =  mPoint.y + measuredHeight / 2;
        if(left < 0) {
            left = 0;
            right = left + measuredWidth;

            x = right / 2;
        }
        if(top < 0) {
            top = 0;
            bottom = top + measuredHeight;

            y = bottom /2;
        }

        if(right > viewWidth) {
            right = viewWidth;
            left = viewWidth - measuredWidth;

            x = (right + left) /2;
        }
        if(bottom > viewHeight) {
            bottom = viewHeight;
            top = bottom - measuredHeight;

            y = (bottom + top) /2;
        }
        view.layout(left, top, right, bottom);

//        if(view.getId() == R.id.tv_point) {
////            if(x != 0 && y != 0) {
////                mPoint.set(x, y);
////            }
//        }else {
//            if(x != 0 && y != 0) {
//                mPoint.set(x, y);
//            }
//        }
    }


    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {

        int pointRadius = 0;
        int backRadius = 0;

        int childCount = getChildCount();
        for(int i = 0; i < childCount ; i++) {
            View view = getChildAt(i);

            if(isOnLayout && isShow) {
                if(view.getId() == R.id.tv_point) {
                    layoutChildView(view, mPoint);
                    if(pointRadius == 0) {
                        pointRadius = view.getWidth();
                    }
                }else {
                    layoutChildView(view, backGroundPoint);
                    if(backRadius == 0) {
                        backRadius = view.getWidth();
                    }
                }
            }else {
                //手离开屏幕或者第一次的时候,不显示其子View
                view.layout(-100,-100,-1,-1);
            }


        }

        if(pointDistance == 0) {
            if(pointRadius != 0 && backRadius != 0) {
//                pointDistance = (backRadius - pointRadius) /2;
                pointDistance = backRadius /2;
            }
        }
    }

    public void setModel(int model) {
        this.model = model;
    }

    /**
     * 设置轨迹球的监听
     * @param mRudderViewListener 轨迹球的监听
     */
    public void setRudderViewListener(RudderViewListener mRudderViewListener) {
        this.mRudderViewListener = mRudderViewListener;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {

        switch (event.getAction() & MotionEvent.ACTION_MASK){
            case MotionEvent.ACTION_DOWN:
                if(!isOnLayout) isOnLayout = true;

                backGroundPoint.set((int) event.getX(), (int) event.getY());
                mPoint.set(backGroundPoint.x, backGroundPoint.y);
                requestLayout();

                break;
            case MotionEvent.ACTION_MOVE:
                if(!isOnLayout) isOnLayout = true;
                calculateInDontMoveBackPoint((int) event.getX(), (int) event.getY());
                requestLayout();

                break;
            case MotionEvent.ACTION_UP:
                if(isOnLayout) isOnLayout = false;
                requestLayout();
                break;

        }
        notifyListener(event.getAction());
        return true;
    }




    @Override
    public void setVisibility(int visibility) {
        isShow = visibility != View.GONE;
        requestLayout();
    }

    /**
     * 计算背景点和手指移动点的显示位置(底部背景不动)
     */
    private void calculateInDontMoveBackPoint(int x, int y) {
        int yDistance = y - backGroundPoint.y;
        int xDistance = x - backGroundPoint.x;
        int offsetX = Math.abs(xDistance);
        int offsetY = Math.abs(yDistance);
        if (offsetX * offsetX + offsetY * offsetY > pointDistance * pointDistance) {

            if(offsetX == 0) {
                mPoint.y = yDistance > 0 ? backGroundPoint.y + pointDistance : backGroundPoint.y - pointDistance;
            } else
            if(offsetY == 0) {
                mPoint.x = xDistance > 0 ? backGroundPoint.x + pointDistance : backGroundPoint.x - pointDistance;
            }
            else {
                float ver = (float)offsetY / (float)offsetX;

                double calculateX = Math.sqrt(pointDistance * pointDistance / (ver * ver + 1));
                double calculateY = calculateX * ver;
                if(x > backGroundPoint.x )  mPoint.x = backGroundPoint.x + (int)calculateX;
                else mPoint.x = backGroundPoint.x - (int)calculateX;

                if(y > backGroundPoint.y )  mPoint.y = backGroundPoint.y + (int)calculateY;
                else mPoint.y = backGroundPoint.y - (int)calculateY;
            }


        }else {
            mPoint.set(x, y);
        }

    }

    /**
     * 计算背景点个轨迹球的位置
     */
    private void calculateInMoveBackPoint(int x, int y) {

        if(model == MODEL_LEFT_RIGHT) {
            mPoint.set(x, mPoint.y);
        }else if(model == MODEL_UP_DOWN) {
            mPoint.set(mPoint.x, y);
        }else {
            mPoint.set(x, y);
        }
        int yDistance = mPoint.y - backGroundPoint.y;
        int offsetX = Math.abs(mPoint.x - backGroundPoint.x);
        int offsetY = Math.abs(yDistance);

        if (offsetX * offsetX + offsetY * offsetY > pointDistance * pointDistance) {

            if(offsetX == 0) {
                backGroundPoint.y = yDistance > 0 ? mPoint.y - pointDistance : mPoint.y + pointDistance;
            }else {
                float ver = (float)offsetY / (float)offsetX;

                double calculateX = Math.sqrt(pointDistance * pointDistance / (ver * ver + 1));
                double calculateY = calculateX * ver;

                if(calculateX > 1 ) {
                    if(mPoint.x > backGroundPoint.x )  backGroundPoint.x = mPoint.x - (int)calculateX;
                    else backGroundPoint.x = mPoint.x + (int)calculateX;
                }
                if(calculateY > 1) {
                    if(mPoint.y > backGroundPoint.y )  backGroundPoint.y = mPoint.y - (int)calculateY;
                    else backGroundPoint.y = mPoint.y + (int)calculateY;
                }
            }
        }
    }

    /**
     * 通知监听器时轨迹器位置发生了裱花
     * @param mAction touch时间的动作(按下,移动,抬起)
     */
    private void  notifyListener(int mAction) {
        if(mRudderViewListener == null) return;

        int distanceX = mPoint.x - backGroundPoint.x;
        int distanceY = mPoint.y - backGroundPoint.y;

        int offsetX = Math.abs(distanceX);
        int offsetY = Math.abs(distanceY);
        if(pointDistance == 0) return;

        int distance =  (distanceX * distanceX + distanceY * distanceY) * 100 / (pointDistance * pointDistance);


        if(offsetX > offsetY) {
            //左右方向
            if(distanceX > 0 ) {
                //右
                mRudderViewListener.onSteeringWheelChanged(this, RudderViewListener.DIRECTION_RIGHT,distance, mAction);
            }else if(distanceX < 0) {
                //左
                mRudderViewListener.onSteeringWheelChanged(this, RudderViewListener.DIRECTION_LEFT,distance , mAction);
            }
        }else if(offsetX < offsetY) {
            //上下方向
            if(distanceY > 0 ) {
                //下
                mRudderViewListener.onSteeringWheelChanged(this, RudderViewListener.DIRECTION_DOWN,distance, mAction);
            }else if(distanceY < 0) {
                //上
                mRudderViewListener.onSteeringWheelChanged(this, RudderViewListener.DIRECTION_UP,distance, mAction);
            }
        }else if(offsetX == offsetY) {
            //斜对角方向
            if(distanceX > 0 && distanceY < 0) {
                //右上
                mRudderViewListener.onSteeringWheelChanged(this, RudderViewListener.DIRECTION_UP_RIGHT,distance, mAction);
            }
            if(distanceX > 0 && distanceY > 0) {
                //右下
                mRudderViewListener.onSteeringWheelChanged(this, RudderViewListener.DIRECTION_DOWN_RIGHT,distance, mAction);
            }
            if(distanceX < 0 && distanceY < 0) {
                //左上
                mRudderViewListener.onSteeringWheelChanged(this, RudderViewListener.DIRECTION_LEFT_DOWN,distance, mAction);
            }
            if(distanceX < 0 && distanceY > 0) {
                //左下
                mRudderViewListener.onSteeringWheelChanged(this, RudderViewListener.DIRECTION_LEFT_UP,distance, mAction);
            }
        }



    }

    /**
     * 游戏摇杆的位置监听
     */
    public interface RudderViewListener {
        int DIRECTION_UP = 0;
        int DIRECTION_UP_RIGHT = 1;
        int DIRECTION_RIGHT = 2;
        int DIRECTION_DOWN_RIGHT = 3;
        int DIRECTION_DOWN = 4;
        int DIRECTION_LEFT_DOWN = 5;
        int DIRECTION_LEFT = 6;
        int DIRECTION_LEFT_UP = 7;


        /**
         * 方向监听
         * @param direction 方向变化
         * @param distance 距离变化 最大值为100
         */
        void onSteeringWheelChanged(RudderView view, int direction, int distance, int action);
    }
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容