先看效果圖
具體功能:
1.動(dòng)臂碘菜、斗桿蚤认、鏟斗任意角度變換顯示
2.觸摸實(shí)現(xiàn)拖拉效果
實(shí)現(xiàn)步驟:
1.找圖米苹,切圖(PS用的不熟,只能用美圖秀秀傻瓜式切圖)砰琢,最后切得動(dòng)臂蘸嘶,斗桿,鏟斗和挖掘整車部分圖
2.利用美圖秀秀確定各切圖的大小和相應(yīng)的連接點(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)。
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);
//寬度最大化,高度比例縮放
}
}