2.自定義控件之挖掘機(jī)工作裝置姿態(tài)View

先看效果圖


挖掘機(jī)工作裝置姿態(tài)

具體功能:
1.動(dòng)臂碘菜、斗桿蚤认、鏟斗任意角度變換顯示
2.觸摸實(shí)現(xiàn)拖拉效果

實(shí)現(xiàn)步驟:
1.找圖米苹,切圖(PS用的不熟,只能用美圖秀秀傻瓜式切圖)砰琢,最后切得動(dòng)臂蘸嘶,斗桿,鏟斗和挖掘整車部分圖


切圖內(nèi)容

2.利用美圖秀秀確定各切圖的大小和相應(yīng)的連接點(diǎn)位置


圖片尺寸和連接點(diǎn)位置

3.Android自定義View 主要就是onMeasure onLayout onDraw的過程陪汽。本文主要針對(duì)onDraw 過程训唱,在初始化過程中需要計(jì)算屏幕尺寸對(duì)圖片進(jìn)行縮放。
圖片的縮放挚冤,旋轉(zhuǎn)雪情,平移主要通過Matrix實(shí)現(xiàn),對(duì)觸摸的響應(yīng)通過動(dòng)臂你辣、斗桿巡通、鏟斗的矩形區(qū)域確定,而且當(dāng)三個(gè)區(qū)域重疊優(yōu)先級(jí)是鏟斗>斗桿>動(dòng)臂 舍哄。如下圖所示觸摸鏟斗紅色區(qū)域宴凉,優(yōu)先響應(yīng)鏟斗旋轉(zhuǎn)。
觸摸處理.png

4.觸摸移動(dòng)角度和逆時(shí)針順時(shí)針手勢(shì)的判斷表悬,通過acos計(jì)算角度通過與軸心交的前后兩點(diǎn)之間的向量關(guān)系確定順時(shí)針還是逆時(shí)針旋轉(zhuǎn)弥锄。
5.具體代碼

package com.cwd.displayer.ui.widget;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

import androidx.annotation.Nullable;

import com.blankj.utilcode.util.ConvertUtils;
import com.blankj.utilcode.util.ScreenUtils;
import com.cwd.displayer.R;

/**
 * 挖掘機(jī)的動(dòng)臂、斗桿、鏟斗動(dòng)態(tài)圖
 */
public class ExcavatorView extends View {

    private Paint mPaint;
    private float scaleW;
    private float scaleH;
    private float scaleXY;
    private Bitmap catBody;
    private Bitmap catBoom;
    private Bitmap catArm;
    private Bitmap catBucket;

    private int screenW;
    private int screenH;

    private float bodyH;
    private float bodyW;

    private float boomH;
    private float boomW;
    private float armH;
    private float armW;
    private float bucketH;
    private float bucketW;

    private Rect rectBody;
    private Rect rectBoom;
    private Rect rectArm;
    private Rect rectBucket;

    private RectF rectFBody;
    private RectF rectFBoom;
    private RectF rectFArm;
    private RectF rectFBucket;

    float boomCircleX;
    float boomCircleY;
    float armCircleX;
    float armCircleY;
    float bucketCircleX;
    float bucketCircleY;

    float bodyCenterX;
    float bodyCenterY;

    RectF rectFBoomEnd;
    RectF rectFArmEnd;
    RectF rectFBucketEnd;

    public ExcavatorView(Context context) {
        super(context);
        initView();
    }

    public ExcavatorView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        initView();
    }

    public ExcavatorView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView();
    }

    public ExcavatorView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        initView();
    }

    private void initView(){
        mPaint = new Paint();
        mPaint.setAntiAlias(false);
        catBody = BitmapFactory.decodeResource(getResources(),R.drawable.cat_body);
        catBoom = BitmapFactory.decodeResource(getResources(),R.drawable.cat_boom);
        catArm = BitmapFactory.decodeResource(getResources(),R.drawable.cat_arm);
        catBucket = BitmapFactory.decodeResource(getResources(),R.drawable.cat_bucket);
        screenW = ScreenUtils.getScreenWidth();
        screenH = ScreenUtils.getScreenHeight();
        Log.i("xsl_init","screenW="+screenW+",screenH="+screenH);

        scaleW = (float) (845.0/screenW);
        scaleH = (float) (1347.0/screenH);
        Log.i("xsl_measure","scaleW="+scaleW+",scaleH="+scaleH);

        if (scaleW<1 && scaleH<1){
            //如果寬高都?jí)蜃严荆敲床贿M(jìn)行縮放温治,原圖顯示
            scaleXY=1;
        }else {
            //只要寬高有一個(gè)大于1
            //則進(jìn)行縮放
            if(scaleW>=scaleH){
                scaleXY =(float) ((float) Math.round(scaleW*10)/10);
            }else{
                scaleXY =(float) ((float) Math.round(scaleH*10)/10);
            }

        }

        Log.i("xsl_measure","scaleW="+scaleW+",scaleH="+scaleH+",scaleXY="+scaleXY);
        bodyH = catBody.getHeight()/scaleXY;
        bodyW = catBody.getWidth()/scaleXY;
        Log.i("xsl_measure","bodyH="+bodyH+",bodyW="+bodyW);
        boomH = catBoom.getHeight()/scaleXY;
        boomW = catBoom.getWidth()/scaleXY;
        armH = catArm.getHeight()/scaleXY;
        armW = catArm.getWidth()/scaleXY;
        bucketH = catBucket.getHeight()/scaleXY;
        bucketW = catBucket.getWidth()/scaleXY;

        rectBody = new Rect(0,0,catBody.getWidth(),catBody.getHeight());
        rectBoom = new Rect(0,0,catBoom.getWidth(),catBoom.getHeight());
        rectArm = new Rect(0,0,catArm.getWidth(),catArm.getHeight());
        rectBucket = new Rect(0,0,catBucket.getWidth(),catBucket.getHeight());

        rectFBody = new RectF(screenW-bodyW,(screenH-bodyH)/2,screenW,(screenH+bodyH)/2);

        bodyCenterX = screenW-bodyW*3.5f/5;
        bodyCenterY = screenH/2;

        //rectFBoom = new RectF(bodyCenterX-boomW,bodyCenterY-boomH,bodyCenterX,bodyCenterY);
        rectFBoom = new RectF(0,0,catBoom.getWidth(),catBoom.getHeight());
        rectFArm = new RectF(0,0,catArm.getWidth(),catArm.getHeight());
        rectFBucket = new RectF(0,0,catBucket.getWidth(),catBucket.getHeight());

        boomCircleX=bodyCenterX;
        boomCircleY=bodyCenterY-(catBoom.getHeight()-38)/scaleXY;


        destBoomC = boomC.clone();
        destArmC = armC1.clone();
        armDestC2 = armC2.clone();
        destBucketC = bucketC1.clone();
        bucketDestC2 = bucketC2.clone();

        rectFBoomEnd=new RectF();
        rectFArmEnd=new RectF();
        rectFBucketEnd=new RectF();

        boomMatrix = new Matrix();
        armMatrix = new Matrix();
        bucketMatrix = new Matrix();


        double x = getDegree(new Point(0,0),new Point(1,0),new Point(0,1));
        double y = getDegree(new Point(0,0),new Point(0,1),new Point(1,0));

        Log.i("xsl_measure","xDegree="+x+",yDegree="+y);

    }

    //動(dòng)臂角度和中心點(diǎn)
    int degreeBoom =0;
    float[] boomC=new float[]{280.0f,38.0f};
    float[] destBoomC;

    //斗桿角度和中心點(diǎn)
    int degreeArm =0;
    float[] armC1=new float[]{15.0f,81.0f};//動(dòng)臂——斗桿軸點(diǎn) 動(dòng)臂上
    float[] destArmC;
    float[] armC2=new float[]{65.0f,15.0f};//動(dòng)臂——斗桿軸點(diǎn) 斗桿上
    float[] armDestC2;

    //鏟斗的角度和中心
    int degreeBucket =0;
    float[] bucketC1=new float[]{223.0f,68.0f};//斗桿-鏟斗軸點(diǎn) 斗桿上
    float[] destBucketC;
    float[] bucketC2=new float[]{58.0f,58.0f};//斗桿-鏟斗軸點(diǎn) 鏟斗上
    float[] bucketDestC2;

    final int TypeBoom=1;
    final int TypeArm=2;
    final int TypeBucket=3;



    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        Paint paint = new Paint();
        paint.setColor(0xffff0000);
        paint.setStrokeWidth(6f);

        /***********動(dòng)臂相關(guān)計(jì)算**********/
        Matrix boomMatrix = new Matrix();
        boomMatrix.preScale((float) 1.0/scaleXY,(float) 1.0/scaleXY);
        boomMatrix.preRotate(degreeBoom,boomCircleX,boomCircleY);
        boomMatrix.mapPoints(destBoomC,boomC);
        Log.i("xsl","degreeBoom="+degreeBoom);
        Log.i("xsl","[x,y]=["+destBoomC[0]+","+destBoomC[1]+"]");
        Log.i("xsl","[boomCircleX,boomCircleY]=["+boomCircleX+","+boomCircleY+"]");
        boomMatrix.postTranslate(boomCircleX-destBoomC[0],boomCircleY-destBoomC[1]);
        boomMatrix.mapRect(rectFBoomEnd,rectFBoom);
        boomMatrix.mapPoints(destArmC,armC1);//斗桿的軸點(diǎn)

        /************斗桿相關(guān)計(jì)算**********/
        Matrix armMatrix = new Matrix();
        armMatrix.preScale((float) 1.0/scaleXY,(float) 1.0/scaleXY);
        //以動(dòng)臂上的斗桿連接點(diǎn)為軸心
        armMatrix.preRotate(degreeBoom+degreeArm,destArmC[0],destArmC[1]);
        //斗桿圖片上的連接點(diǎn)
        armMatrix.mapPoints(armDestC2,armC2);
        armMatrix.postTranslate(destArmC[0]-armDestC2[0],destArmC[1]-armDestC2[1]);
        armMatrix.mapRect(rectFArmEnd,rectFArm);
        armMatrix.mapPoints(destBucketC,bucketC1);//鏟斗的軸心
        /*******鏟斗相關(guān)計(jì)算********/
        Matrix bucketMatrix = new Matrix();
        bucketMatrix.preScale((float) 1.0/scaleXY,(float) 1.0/scaleXY);
        //以斗桿上的鏟斗連接點(diǎn)為軸心
        bucketMatrix.preRotate(degreeBoom+degreeArm+degreeBucket,destBucketC[0],destBucketC[1]);
        //鏟斗圖片上的連接點(diǎn)
        bucketMatrix.mapPoints(bucketDestC2,bucketC2);
        bucketMatrix.postTranslate(destBucketC[0]-bucketDestC2[0],destBucketC[1]-bucketDestC2[1]);
        bucketMatrix.mapRect(rectFBucketEnd,rectFBucket);



        //畫工作裝置的區(qū)域
        paint.setColor(0xff00ff00);
        canvas.drawRect(rectFBoomEnd,paint);
        paint.setColor(0xff0000ff);
        canvas.drawRect(rectFArmEnd,paint);
        paint.setColor(0xffff0000);
        canvas.drawRect(rectFBucketEnd,paint);
        //畫鏟斗
        canvas.drawBitmap(catBucket,bucketMatrix,mPaint);
        //畫斗桿
        canvas.drawBitmap(catArm,armMatrix,mPaint);
        //畫動(dòng)臂
        canvas.drawBitmap(catBoom,boomMatrix,mPaint);
        //畫車輛
        canvas.drawBitmap(catBody,
                rectBody,
                rectFBody,mPaint);
        //動(dòng)臂的軸點(diǎn)
        paint.setColor(0xff000000);
        paint.setStyle(Paint.Style.FILL_AND_STROKE);
//        canvas.drawPoint(boomCircleX,boomCircleY,paint);
//        canvas.drawPoint(destArmC[0],destArmC[1],paint);
//        canvas.drawPoint(destBucketC[0],destBucketC[1],paint);
        canvas.drawCircle(boomCircleX,boomCircleY,1,paint);
        canvas.drawCircle(destArmC[0],destArmC[1],1,paint);
        canvas.drawCircle(destBucketC[0],destBucketC[1],1,paint);

//        degreeBoom--;
//        degreeArm--;
//        degreeBucket--;
//        invalidate();


    }

    private Matrix boomMatrix;
    private Matrix armMatrix;
    private Matrix bucketMatrix;


    float posX=0f;
    float posY=0f;
    float posX_old=0f;
    float posY_old=0f;

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        posX = (float) event.getX();
        posY = (float) event.getY();
        if (event.getAction()==MotionEvent.ACTION_DOWN){
            posX_old = (float) event.getX();
            posY_old = (float) event.getY();
        }
        if (event.getAction()==MotionEvent.ACTION_MOVE) {

            posX = (float) event.getX();
            posY = (float) event.getY();

            if (rectFBucketEnd.contains(posX, posY)) {
                //degreeBucket =
                double degree = getDegree(new Point((int)destBucketC[0],(int)destBucketC[1]),
                        new Point((int)posX,(int)posY),
                        new Point((int)posX_old,(int)posY_old)
                        );
                degreeBucket =(int) (degreeBucket+degree);


            } else if (rectFArmEnd.contains(posX, posY)) {
                double degree = getDegree(new Point((int)destArmC[0],(int)destArmC[1]),
                        new Point((int)posX,(int)posY),
                        new Point((int)posX_old,(int)posY_old)
                );
                degreeArm =(int) (degreeArm+degree);
            } else if (rectFBoomEnd.contains(posX, posY)) {
                double degree = getDegree(new Point((int)boomCircleX,(int)boomCircleY),
                        new Point((int)posX,(int)posY),
                        new Point((int)posX_old,(int)posY_old)
                );

                degreeBoom = (int) (degreeBoom+degree);
            }

            posX_old = posX;
            posY_old = posY;
        }

        if (event.getAction()==MotionEvent.ACTION_UP){
          posX=0f;
          posY=0f;
          posX_old=0f;
          posY_old=0f;
        }

        invalidate();
        return true;
    }

    private double getDegree(Point o,Point s,Point e){

        double cosfi = 0;
        double fi = 0;
        double norm = 0;
        double dsx = s.x - o.x;
        double dsy = s.y - o.y;
        double dex = e.x - o.x;
        double dey = e.y - o.y;
        cosfi = dsx * dex + dsy * dey;
        norm = (dsx * dsx + dsy * dsy) * (dex * dex + dey * dey);
        cosfi /= Math.sqrt(norm);
        if (cosfi >= 1.0) return 0;
        if (cosfi <= -1.0) return Math.PI;
        fi = Math.acos(cosfi);
        int temp = (s.x-o.x)*(e.y-o.y)-(s.y-o.y)*(e.x-o.x);

        if (180 * fi / Math.PI < 180) {
            if (temp<=0) {
                return 180 * fi / Math.PI;
            }else{
                return 180 * fi*(-1) / Math.PI;
            }
        } else {
            if (temp<=0) {
                return 360 - 180 * fi / Math.PI;
            }else{
                return (360 - 180 * fi / Math.PI)*(-1);
            }
        }
    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //計(jì)算最小的長(zhǎng)寬高 540*960
        //屏幕小于那個(gè)尺寸的時(shí)候進(jìn)行比例縮放
        //body     256*169
        //boom     280*91
        //arm      228*74
        //bucker   81*67
        //動(dòng)臂+斗桿+鏟斗==589
        //寬最大 589+256 = 845
        //高最大 169+ 589*2=1347

        //screenW = ScreenUtils.getScreenWidth();
        //screenH = ScreenUtils.getScreenHeight();
//        Log.i("xsl_measure","screenW="+screenW+",screenH="+screenH);
//
//        scaleW = (float) (845.0/screenW);
//        scaleH = (float) (1347.0/screenH);
//
//        Log.i("xsl_measure","scaleW="+scaleW+",scaleH="+scaleH);


        //寬度最大化,高度比例縮放



    }
}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末戒悠,一起剝皮案震驚了整個(gè)濱河市熬荆,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌绸狐,老刑警劉巖卤恳,帶你破解...
    沈念sama閱讀 216,470評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異寒矿,居然都是意外死亡突琳,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門符相,熙熙樓的掌柜王于貴愁眉苦臉地迎上來拆融,“玉大人,你說我怎么就攤上這事啊终【当” “怎么了?”我有些...
    開封第一講書人閱讀 162,577評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵孕索,是天一觀的道長(zhǎng)逛艰。 經(jīng)常有香客問我,道長(zhǎng)搞旭,這世上最難降的妖魔是什么散怖? 我笑而不...
    開封第一講書人閱讀 58,176評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮肄渗,結(jié)果婚禮上镇眷,老公的妹妹穿的比我還像新娘。我一直安慰自己翎嫡,他們只是感情好欠动,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,189評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著惑申,像睡著了一般具伍。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上圈驼,一...
    開封第一講書人閱讀 51,155評(píng)論 1 299
  • 那天人芽,我揣著相機(jī)與錄音,去河邊找鬼绩脆。 笑死萤厅,一個(gè)胖子當(dāng)著我的面吹牛橄抹,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播惕味,決...
    沈念sama閱讀 40,041評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼楼誓,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了名挥?” 一聲冷哼從身側(cè)響起疟羹,我...
    開封第一講書人閱讀 38,903評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎躺同,沒想到半個(gè)月后阁猜,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體丸逸,經(jīng)...
    沈念sama閱讀 45,319評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蹋艺,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,539評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了黄刚。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片捎谨。...
    茶點(diǎn)故事閱讀 39,703評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖憔维,靈堂內(nèi)的尸體忽然破棺而出涛救,到底是詐尸還是另有隱情,我是刑警寧澤业扒,帶...
    沈念sama閱讀 35,417評(píng)論 5 343
  • 正文 年R本政府宣布检吆,位于F島的核電站,受9級(jí)特大地震影響程储,放射性物質(zhì)發(fā)生泄漏蹭沛。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,013評(píng)論 3 325
  • 文/蒙蒙 一章鲤、第九天 我趴在偏房一處隱蔽的房頂上張望摊灭。 院中可真熱鬧,春花似錦败徊、人聲如沸帚呼。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽煤杀。三九已至,卻和暖如春沪哺,著一層夾襖步出監(jiān)牢的瞬間沈自,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工凤粗, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留酥泛,地道東北人今豆。 一個(gè)月前我還...
    沈念sama閱讀 47,711評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像柔袁,于是被迫代替她去往敵國(guó)和親呆躲。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,601評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容