国际惯例,先放效果图(为什么每次都没人气。。。)
这个试衣间的功能呢,其实很简单,最根本的核心就是对事件的处理。
这个项目主要做了以下几件事情:
1、将开源项目SlidingMenu提炼出来,放入在了一个package下,以后直接使用就方便很多,不需要依赖工程(layout和value文件没办法提炼)
2、整个核心功能就三个文件,分别是:FittingView.java、Body.java、Clothes.java,FittingView用来控制事件流,以及总体绘制。Body用于绘制人体,Clothes用于绘制 衣物(暂时只给定了衣服和裤子两种类型)
3、人体、衣服、裤子都是从QQ秀截图而来。
首先我会贴出三个关键文件的源码,源码很简单,有注释,不用我一一讲解。然后最后会给出demo的下载地址。
首先是Body.java文件:
public class Body { private float centerX; private float centerY; private Bitmap bitmap; public Body(Context mContext, float centerX, float centerY) { this.centerX = centerX; this.centerY = centerY; this.bitmap = BitmapFactory.decodeResource(mContext.getResources(), R.mipmap.body); } public void draw(Canvas mCanvas, Paint mPaint){ float left = centerX - bitmap.getWidth() / 2; float top = centerY - bitmap.getHeight() / 2; mCanvas.drawBitmap(bitmap, left, top, mPaint); } }
然后是Clothes.java文件:
public class Clothes { public static final float LEFT = 200; public static final float TOP = 200; private Bitmap mBitmap; private Bitmap mCloseBitmap; private int mBitmapWidth; private int mBitmapHeight; private float left; private float top; private Status status = Status.Default; public Clothes(Context mContext, Bitmap mBitmap, float left, float top) { this.mBitmap = mBitmap; mBitmapWidth = mBitmap.getWidth(); mBitmapHeight = mBitmap.getHeight(); this.left = left; this.top = top; mCloseBitmap = BitmapFactory.decodeResource(mContext.getResources(), R.mipmap.close); } public void move(float dx, float dy){ left += dx; top += dy; } public void draw(Canvas mCanvas, Paint mPaint) { // 绘制图片 mCanvas.drawBitmap(mBitmap, left, top, mPaint); if (status == Status.Select) { //绘制边框 mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeWidth(5); mPaint.setColor(Color.BLUE); mCanvas.drawRect(left, top, left + mBitmapWidth, top + mBitmapHeight, mPaint); //绘制XX mCanvas.drawBitmap(mCloseBitmap, left + mBitmapWidth - mCloseBitmap.getWidth() / 2, top - mCloseBitmap.getHeight() / 2, mPaint); } } public void setStatus(Status status) { this.status = status; } public Status getStatus() { return status; } /** * 是否点击了close图标 * @param x 点击的X坐标 * @param y 点击的Y坐标 * @return 是否点击 */ public boolean isClose(float x, float y){ float mCloseLeft = left + mBitmapWidth - mCloseBitmap.getWidth() / 2; float mCloseRight = left + mBitmapWidth + mCloseBitmap.getWidth() / 2; float mCloseTop = top - mCloseBitmap.getHeight() / 2; float mCloseBottom = top + mCloseBitmap.getHeight() / 2; if (x >= mCloseLeft && x <= mCloseRight && y >= mCloseTop && y <= mCloseBottom) { return true; } else { return false; } } /** * 是否点击到了图片 * @param x 点击的X坐标 * @param y 点击的Y坐标 * @return 是否点击 */ public boolean isContainer(float x, float y) { if (x >= left && x <= left + mBitmapWidth && y >= top && y <= top + mBitmapHeight) { return true; } else { return false; } } public static enum Status { Default, Select } }
最后就是FittingView.java文件,整个的逻辑控制、事件处理都在这里:
public class FittingView extends View { private Body body; private List<Clothes> clotheses = new ArrayList<Clothes>(); private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); private float[] movePosition = new float[2]; public FittingView(Context context) { super(context); } public FittingView(Context context, AttributeSet attrs) { super(context, attrs); } public FittingView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } public void addClothes(Clothes clothes){ clotheses.add(clothes); invalidate(); } public void removeClothes(Clothes clothes){ clotheses.remove(clothes); invalidate(); } @Override protected void onDraw(Canvas canvas) { //绘制人体 if(body != null){ body.draw(canvas, mPaint); } Iterator<Clothes> it = clotheses.iterator(); while(it.hasNext()){ it.next().draw(canvas ,mPaint); } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { float[] centerPosition = new float[2]; centerPosition[0] = MeasureSpec.getSize(widthMeasureSpec) / 2; centerPosition[1] = MeasureSpec.getSize(heightMeasureSpec) / 2; if(centerPosition[0] != 0 && centerPosition[1] != 0){ body = new Body(getContext(), centerPosition[0], centerPosition[1]); } super.onMeasure(widthMeasureSpec, heightMeasureSpec); } @Override public boolean onTouchEvent(MotionEvent event) { Clothes clothes = null; switch (event.getAction()) { case MotionEvent.ACTION_DOWN: boolean isTouchOnClothes = false; //设置选中状态 Iterator<Clothes> it = clotheses.iterator(); while(it.hasNext()){ clothes = it.next(); if (clothes.isContainer(event.getX(), event.getY())) { isTouchOnClothes = true; setSelectStatus(clothes); invalidate(); } } //若没有选中,则关闭所有选中状态 if(!isTouchOnClothes){ setDefaultStatus(); invalidate(); } //赋值XY movePosition[0] = event.getX(); movePosition[1] = event.getY(); return true; case MotionEvent.ACTION_MOVE: clothes = getSelectStatus(); if(clothes != null){ //跟随移动 float dx = event.getX() - movePosition[0]; float dy = event.getY() - movePosition[1]; clothes.move(dx, dy); invalidate(); movePosition[0] = event.getX(); movePosition[1] = event.getY(); } break; case MotionEvent.ACTION_UP: clothes = getSelectStatus(); if(clothes != null){ if(clothes.isClose(event.getX(), event.getY())){ removeClothes(clothes); invalidate(); } } break; } return super.onTouchEvent(event); } private void setDefaultStatus(){ Iterator<Clothes> it = clotheses.iterator(); while(it.hasNext()){ it.next().setStatus(Clothes.Status.Default); } } private void setSelectStatus(Clothes clothes){ setDefaultStatus(); clothes.setStatus(Clothes.Status.Select); } private Clothes getSelectStatus(){ Iterator<Clothes> it = clotheses.iterator(); while(it.hasNext()){ Clothes clothes = it.next(); if(clothes.getStatus() == Clothes.Status.Select){ return clothes; } } return null; } }
最后是Demo的地址:(工程是Android Studio的,Eclipse只能自己辛苦了)
点我下载Demo