自定義遙感控件

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閱讀 230,527評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,687評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 178,640評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,957評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,682評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,011評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,009評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,183評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,714評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,435評論 3 359
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,665評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,148評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,838評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,251評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,588評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,379評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,627評論 2 380

推薦閱讀更多精彩內容