Android试衣间

国际惯例,先放效果图(为什么每次都没人气。。。)



这个试衣间的功能呢,其实很简单,最根本的核心就是对事件的处理。

这个项目主要做了以下几件事情:

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
    }
}


Clothes类就比Body稍微要复杂一点点,因为Clothes除了要显示,还有状态之分,分为普通状态和选中状态,普通状态不能操作,只有在选中状态才可以进行移动、删除的操作。

最后就是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,源码一看便懂,不需要讲解。

最后是Demo的地址:(工程是Android Studio的,Eclipse只能自己辛苦了)

点我下载Demo

你可能感兴趣的:(android,自定义控件,试衣间)