Android自定義View初探(二)——仿360垃圾清理

明天就是五一勞動節了,在這裏先祝各位程序猿勞動節快樂,別在加班了!

自從嘗試過寫自定義View(Android自定義View初探(一)——餅圖)之後,每當看到別人的應用時,總是在想別人的實現方式,或許,這就是程序猿的悲哀吧O(∩_∩)O~。

前兩天就想嘗試去用自定義View實現360的垃圾清理界面了,只是最近一直在忙dicuz自定義修改,所以就先放下了。不過馬上放五一了,沒太多事,今天就來做一些新的嘗試吧。

我這裏既然寫到了是“仿”360,所以大家千萬別在看完後丟磚頭哈,畢竟我只看到了它的表象,誰知道它是個神馬呢?

其實當時看完它的界面後覺得應該就是簡單的背景色切換隨機元素的繪製:

1.剛進界面的時候是一種顏色,比如深綠,點擊“開始掃描”(360可木有,他是自動掃描);
2.當垃圾的數量達到一定值後,比如100MB,背景色變色,比如藍色,;當垃圾值達到200MB的時候再變色,比如紅色;
3.垃圾值的增量(就是那些飛來飛去的小垃圾塊)的位置是隨機的,只不過蒐集垃圾時,座標是從四面往中間移動;釋放垃圾時,座標是從中間往四面移動(效果可自己隨意定);
4.釋放垃圾時,按鈕變成了進度條(這個可以實現,但我沒做^_^)。
5.還有些細節有待發現。。。

說了這麼多,大家也不知道我在說什麼^_^,我就不嘮叨了,代碼有註釋。
照例先上效果圖:

初始狀態
初始狀態

蒐集狀態-小於100MB
蒐集狀態-小於100MB

蒐集狀態-大於100MB小於300MB
蒐集狀態-大於100MB小於300MB

蒐集狀態-大於300MB
蒐集狀態-大於300MB

蒐集完成狀態
蒐集完成狀態

釋放狀態和蒐集狀態的顏色變化是相同的,不同的是小垃圾的移動方式,這裏就不貼圖片了,太多了。

下面就貼大家最喜歡大代碼了^_^:

/**
 * @author MR.yan
 * 
 */
public class ShaderView extends View
{
    /**
     * 繪畫初始狀態
     */
    private static final int STATE_DRAW_INIT = 1;
    /**
     * 繪畫垃圾蒐集中狀態
     */
    private static final int STATE_DRAW_COLLECTING_GABAGE = STATE_DRAW_INIT + 1;
    /**
     * 繪畫垃圾蒐集完成狀態
     */
    private static final int STATE_DRAW_COLLECTED_GABAGE = STATE_DRAW_INIT + 2;
    /**
     * 繪畫垃圾搜釋放中狀態
     */
    private static final int STATE_DRAW_RELEASE_GABAGE = STATE_DRAW_INIT + 3;
    /**
     * 默認水平間距(dp)
     */
    private static final float DEFAULT_WIDTH_PADDING = 15;
    /**
     * 默認垂直間距(dp)
     */
    private static final float DEFAULT_HEIGHT_PADDING = 5;
    /**
     * 畫筆
     */
    private Paint paint;
    /**
     * 屏幕密度
     */
    private float density;
    /**
     * 屏幕寬度px
     */
    private float width;
    /**
     * 屏幕高度px
     */
    private float height;
    /**
     * 當前繪畫狀態
     */
    private int mState = STATE_DRAW_INIT;
    /**
     * 圓角矩形
     */
    private RectF rectF;
    /**
     * 矩形
     */
    private Rect rect;
    /**
     * 隨機數器
     */
    private Random random = new Random();
    /**
     * 已蒐集的垃圾總數
     */
    private float gabageSize = 0.0f;
    /**
     * 垃圾邏輯處理的線程開關
     */
    private boolean isRun;
    /**
     * 小垃圾的x座標(px)
     */
    private float littleGabageX;
    /**
     * 小垃圾的y座標(px)
     */
    private float littleGabageY;
    /**
     * 每次隨機產生的小垃圾數量
     */
    private float tempgabage;
    /**
     * 已清理的總的垃圾數量
     */
    private float allGabages;
    /**
     * 小數格式化工具
     */
    private DecimalFormat decimalFormat;
    /**
     * 初始化標識
     */
    private boolean isInit;
    /**
     * 背景色
     */
    private int bgColor = Color.parseColor("#29A600");

    public ShaderView(Context context)
    {
        this(context, null);
    }

    public ShaderView(Context context, AttributeSet attrs)
    {
        this(context, attrs, 0);
    }

    public ShaderView(Context context, AttributeSet attrs, int defStyle)
    {
        super(context, attrs, defStyle);
        init(context);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b)
    {
        super.onLayout(changed, l, t, r, b);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
    {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
    /**
     * 初始化
     * 
     * @param c 上下文
     */
    private void init(Context c)
    {
        density = c.getResources().getDisplayMetrics().density;
        paint = new Paint();
        rectF = new RectF();
        rect = new Rect();
        decimalFormat = new DecimalFormat();
        decimalFormat.setMaximumFractionDigits(2);
    }

    /**
     * 畫背景色
     * 
     * @param canvas
     */
    private void drawBackground(Canvas canvas)
    {
        if (!isInit)
        {
            isInit = !isInit;
            width = getWidth();
            height = getHeight();
        }
        paint.setColor(bgColor);
        canvas.drawRect(0, 0, width, height, paint);
    }

    /**
     * 畫位置不變的部分
     * 
     * @param canvas 畫布
     * @param gabages 蒐集的垃圾數量
     * @param title 垃圾數量上面的標題
     * @param endDescription 垃圾數量右下角的描述
     * @param uinTg 垃圾的單位(MB、GB)
     * @param btnDescription 按鈕文本
     */
    private void drawStationaryChildren(Canvas canvas, String gabages, String title, String endDescription, String uinTg, String btnDescription)
    {
        resetPaint();
        paint.setColor(Color.WHITE);
        paint.setTextSize(15 * density);
        canvas.drawText(title, width / 5, height / 3, paint);

        paint.setTextSize(75 * density);
        paint.setTypeface(Typeface.create("System", Typeface.BOLD));
        paint.getTextBounds(gabages, 0, gabages.length(), rect);
        canvas.drawText(gabages, width / 4, height / 3 + 2 * rect.height(), paint);

        paint.setTextSize(18 * density);
        paint.setTypeface(Typeface.create("System", Typeface.NORMAL));
        canvas.drawText(uinTg, width / 4 + rect.width() + DEFAULT_WIDTH_PADDING / 5 * density, height / 3 + rect.height(), paint);

        canvas.drawText(endDescription, width / 4 + rect.width() + DEFAULT_WIDTH_PADDING / 5 * density, height / 3 + 2 * rect.height()
                + DEFAULT_HEIGHT_PADDING * density, paint);

        paint.setColor(Color.parseColor("#50B62E"));
        rectF.set(width / 4, height - DEFAULT_HEIGHT_PADDING * 24 * density, width - width / 4, height - DEFAULT_HEIGHT_PADDING * 15 * density);
        canvas.drawRoundRect(rectF, 55f, 55f, paint);
        //想畫進度條的可以在這裏再畫一個矩形覆蓋在原來矩形上面,新矩形的寬度是已清理的垃圾數量

        paint.setColor(Color.WHITE);
        paint.setTextSize(25 * density);
        paint.getTextBounds(btnDescription, 0, btnDescription.length(), rect);
        canvas.drawText(btnDescription, width / 2 - rect.width() / 2, height - DEFAULT_HEIGHT_PADDING * 15 * density - rect.height() / 2, paint);
    }

    /**
     * 畫不固定的小垃圾
     * 
     * @param canvas 畫布
     * 
     */
    private void drawNotFixChildren(Canvas canvas)
    {
        if (mState != STATE_DRAW_COLLECTING_GABAGE && mState != STATE_DRAW_RELEASE_GABAGE)
            return;
        resetPaint();
        String tempGabageStr = tempgabage + "MB";
        Rect ry = new Rect();

        paint.setColor(bgColor);
        paint.setTextSize(16 * density);
        paint.getTextBounds(tempGabageStr, 0, tempGabageStr.length(), ry);
        RectF rx = new RectF(littleGabageX, littleGabageY - 2 * ry.height(), littleGabageX + ry.width(), littleGabageY);//不建議在這裏new矩形,如果放在onDraw方法裏,會有警告
        canvas.drawRoundRect(rx, 2, 2, paint);
        paint.setColor(Color.WHITE);
        canvas.drawText(tempGabageStr, littleGabageX, littleGabageY, paint);
    }

    /**
     * 畫筆重置
     */
    private void resetPaint()
    {
        paint.reset();
        paint.setAntiAlias(true);
    }

    /**
     * 繪畫的邏輯處理器
     */
    private Runnable drawRunnable = new Runnable()
    {
        public void run()
        {
            // 線程暫停或當蒐集垃圾時蒐集的數量大於500或當釋放垃圾時剩餘的垃圾數量小於0
            if (!isRun || (mState == STATE_DRAW_COLLECTING_GABAGE && gabageSize > 500) || (mState == STATE_DRAW_RELEASE_GABAGE && gabageSize < 0))
            {
                switch (mState)
                {
                    case STATE_DRAW_COLLECTING_GABAGE:
                        mState = STATE_DRAW_COLLECTED_GABAGE;
                        allGabages += gabageSize;
                        break;
                    case STATE_DRAW_RELEASE_GABAGE:
                        mState = STATE_DRAW_INIT;
                        break;
                    default:
                        break;
                }
                postInvalidate();
                return;
            }
            switch (mState)
            {
                case STATE_DRAW_COLLECTING_GABAGE:
                    dealCollectGabage();
                    break;
                case STATE_DRAW_RELEASE_GABAGE:
                    dealRelaseGabage();
                    break;
                default:
                    break;
            }
            // 改變背景色的判斷
            if (gabageSize <= 100)
                bgColor = Color.GREEN;
            else if (gabageSize <= 300)
                bgColor = Color.MAGENTA;
            else bgColor = Color.RED;

            postInvalidate();
            postDelayed(this, 5);
        }
    };

    /**
     * 處理垃圾蒐集
     */
    private void dealCollectGabage()
    {
        if (littleGabageX >= (width / 2 - 50) && littleGabageX <= (width / 2 + 50) && littleGabageY >= (height / 2 - 50)
                && littleGabageY <= (height / 2 + 50))
        {
            resetCollectDatas();
        }
        else
        {
            if (littleGabageX > width / 2)
                littleGabageX -= 43.000024;
            else if (littleGabageX < width / 2)
                littleGabageX += 33.000024;
            if (littleGabageY > height / 2)
                littleGabageY -= 33.000024;
            else if (littleGabageY < height / 2)
                littleGabageY += 33.000024;
        }
    }

    /**
     * 處理垃圾釋放
     */
    private void dealRelaseGabage()
    {
        if ((littleGabageX <= 50 || littleGabageX >= width - 50) && (littleGabageY <= 50 || littleGabageY >= height - 50))
        {
            resetReleaseDatas();
        }
        else
        {
            if (littleGabageX > width / 2)
                littleGabageX += 63.000024;
            else if (littleGabageX < width / 2)
                littleGabageX -= 63.000024;
            if (littleGabageY > height / 2)
                littleGabageY += 63.000024;
            else if (littleGabageY < height / 2)
                littleGabageY -= 63.000024;
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event)
    {
        switch (event.getAction())
        {
            case MotionEvent.ACTION_DOWN:
                float dX = event.getX();
                float dY = event.getY();
                // 比對當前按下的座標和上面畫的按鈕(圓角矩形)的座標範圍,在按鈕座標範圍內則觸發
                if ((dX >= width / 4 && dX <= width - width / 4)
                        && (dY <= height - DEFAULT_HEIGHT_PADDING * 15 * density && dY >= height - DEFAULT_HEIGHT_PADDING * 24 * density))
                {
                    switch (mState)
                    {
                        case STATE_DRAW_INIT:
                            mState = STATE_DRAW_COLLECTING_GABAGE;
                            startCollectGabage();
                            break;
                        case STATE_DRAW_COLLECTED_GABAGE:
                            mState = STATE_DRAW_RELEASE_GABAGE;
                            startReleaseGabage();
                            break;
                        default:
                            break;
                    }
                }
                break;
            default:
                break;
        }
        return super.onTouchEvent(event);
    }

    /**
     * 開始處理垃圾蒐集
     */
    private void startCollectGabage()
    {
        gabageSize = 0;
        resetCollectDatas();
        postDelayed(drawRunnable, 10);
        if (!isRun)
            isRun = true;
    }

    /**
     * 開始處理垃圾釋放
     */
    private void startReleaseGabage()
    {
        resetReleaseDatas();
        postDelayed(drawRunnable, 10);
        if (!isRun)
            isRun = true;
    }

    /**
     * 重置釋放垃圾時小垃圾座標和垃圾值等
     */
    private void resetReleaseDatas()
    {
        int c = random.nextInt(4);
        switch (c)
        {
            case 0:
                littleGabageX = width / 2 + 5;
                littleGabageY = height / 2 + 5;
                break;
            case 1:
                littleGabageX = width / 2 - 5;
                littleGabageY = height / 2 - 5;
                break;
            case 2:
                littleGabageX = width / 2 + 5;
                littleGabageY = height / 2 - 5;
                break;
            case 3:
                littleGabageX = width / 2 - 5;
                littleGabageY = height / 2 + 5;
                break;
            default:
                break;
        }
        tempgabage = -5 - (float) random.nextInt(15);
        gabageSize += tempgabage;
    }

    /**
     * 重置蒐集垃圾時小垃圾座標和垃圾值等
     */
    private void resetCollectDatas()
    {
        littleGabageX = 100 + (float) random.nextInt((int) width - 200);
        littleGabageY = 100 + (float) random.nextInt((int) height - 200);
        tempgabage = 5 + (float) random.nextInt(15);
        gabageSize += tempgabage;
    }

    @Override
    protected void onDraw(Canvas canvas)
    {
        resetPaint();
        String gabage = "", title = "", endTag = "", btnDescription = "";
        float gabg = 0;
        switch (mState)
        {
            case STATE_DRAW_INIT:
                bgColor = Color.parseColor("#29A600");
                gabg = allGabages;
                title = "累計清理 : ";
                endTag = "垃圾文件";
                btnDescription = "開始掃描";
                break;
            case STATE_DRAW_COLLECTING_GABAGE:
                gabg = gabageSize;
                title = "";
                btnDescription = "正在掃描...";
                endTag = "建議清理";
                break;
            case STATE_DRAW_COLLECTED_GABAGE:
                gabg = gabageSize;
                title = "";
                endTag = "建議清理";
                btnDescription = "一鍵清理";
                break;
            case STATE_DRAW_RELEASE_GABAGE:
                gabg = gabageSize;
                title = "";
                endTag = "建議清理";
                btnDescription = "正在清理...";
                break;
        }

        gabage = gabg >= 1000 ? decimalFormat.format(gabg / 1024) : String.valueOf(gabg);
        drawBackground(canvas);
        drawNotFixChildren(canvas);
        drawStationaryChildren(canvas, gabage, title, endTag, gabg >= 1000 ? "GB" : "MB", btnDescription);
    }
}

整個視圖的大致思路、效果和代碼就是這樣子了,我依然還是處於嘗試的過程中,肯定有很多地方有不足之處,希望大家交流討論,共同進步!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章