1. 程式人生 > >自定義View實戰

自定義View實戰

PS:上一篇從0開始學自定義View有博友給我留言說要看實戰,今天我特意寫了幾個例子,供大家參考,所畫的圖案加上動畫看著確實讓人舒服,喜歡的博友可以直接拿到自己的專案中去使用,由於我這個寫的是demo,程式碼格式寫的有些亂,所以,要自己封裝一下才可以使用,當然你如果真的不想封裝,可以直接使用,也可以給我留言,我封裝好放在github上供大家參考,也會做成依賴讓大家直接新增即可

先上圖再分析

可以看出圖中有三種樣式

  • 第一種是普通的一個label,使用場景:商品過期,促銷等展示。
  • 第二種是圓形進度條,      使用場景:下載檔案進度,載入視訊進度,耗電量進度.....
  • 第三種是條形進度條,      使用場景:滑動調值,手機音效大小...

上面的三種,均是demo,考慮使用場景並不完善,比如說第三種條形進度條還可以加上刻度,滑動到兩邊需要判斷越界等。那就先拿第三個來吧

條形進度條-可拖動

分析:我們想要做一個類似的控制元件,需要考慮的問題不只是眼睛看的到的,看不到的就好比我只能點選小紅球才可以滑動,我點選其他區域是不能有任何操作的,這個時候就要判斷手指down的時候是否落在了小球上。

  • 線條 : 漸變顏色,線帽格式,長度,寬度設定,父佈局寬高格式設定格式設定,子view寬高格式設定
  • 球 :顏色,起始位置和終止位置要在線上,尺寸

在做之前我們先一個一個知識點解析,首先是線的漸變顏色,單獨拿出

/**
     * 設定進度圓環顏色(支援漸變色)
     *
     * @param colorArray 漸變色集合
     */
    private int[] mColorArray;  // 圓環漸變色
    public void setProgColor(@ColorRes int[] colorArray) {
        if (colorArray == null || colorArray.length < 2) return;
        mColorArray = new int[colorArray.length];
        for (int index = 0; index < colorArray.length; index++)
            mColorArray[index] = ContextCompat.getColor(getContext(), colorArray[index]);
        paint.setShader(new LinearGradient(0, 0, getMeasuredWidth(),0 , mColorArray, new float[]{0,.3F,.6F,.9F}, Shader.TileMode.MIRROR));
        invalidate();
    }

我們可以看到我封裝成了一個方法,通過paint.setShader進行著色,方法傳入的是LinearGradient物件,我們看原始碼,解釋引數

 /**
     * Create a shader that draws a linear gradient along a line.
     *
     * @param x0           The x-coordinate for the start of the gradient line
     * @param y0           The y-coordinate for the start of the gradient line
     * @param x1           The x-coordinate for the end of the gradient line
     * @param y1           The y-coordinate for the end of the gradient line
     * @param colors       The colors to be distributed along the gradient line
     * @param positions    May be null. The relative positions [0..1] of
     *                     each corresponding color in the colors array. If this is null,
     *                     the the colors are distributed evenly along the gradient line.
     * @param tile         The Shader tiling mode
    */
    public LinearGradient(float x0, float y0, float x1, float y1, @NonNull @ColorInt int colors[],
            @Nullable float positions[], @NonNull TileMode tile) {
        if (colors.length < 2) {
            throw new IllegalArgumentException("needs >= 2 number of colors");
        }
        if (positions != null && colors.length != positions.length) {
            throw new IllegalArgumentException("color and position arrays must be of equal length");
        }
        mType = TYPE_COLORS_AND_POSITIONS;
        mX0 = x0;
        mY0 = y0;
        mX1 = x1;
        mY1 = y1;
        mColors = colors.clone();
        mPositions = positions != null ? positions.clone() : null;
        mTileMode = tile;
    }

引數

  1. x0,y0著色的起始位置
  2. x1,y1終止位置
  3. colors區域內著色的顏色集
  4. positions區域內部劃分模組,逐一著色,如:區域1-100劃分為4塊,第一塊1-25紅色,26-50藍色..。系統會預設在兩種顏色不一樣的情況下進行顏色過度渲染,達到漸變的效果,所以我們不用擔心出現紅藍劃分明顯的情況。
  5. TileMode 模式選擇
    1.   CLAMP:當圖片小於繪製尺寸時要進行邊界拉伸來填充
    2.   REPEAT:當圖片小於繪製尺寸時重複平鋪
    3.   MIRROR:當圖片小於繪製尺寸時映象平鋪

好了,下面我們就先畫線和紅點,如果有看不懂的博友,可以先看上一篇從0開始學自定義View。

public void init(){
        paint = new Paint();
        paint.setStrokeWidth(20);//畫筆寬度
        paint.setStyle(Paint.Style.FILL);//填充型別
        paint.setAntiAlias(true);
        paint.setStrokeCap(Paint.Cap.ROUND);//線帽,半圓
        paintCircle2 = new Paint();
        paintCircle2.setColor(Color.RED);
        paintCircle2.setStrokeWidth(5);
        paintCircle2.setStyle(Paint.Style.FILL);
        paintCircle2.setAntiAlias(true);
    }
 @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        width = MeasureSpec.getSize(widthMeasureSpec);
        heigth =  MeasureSpec.getSize(heightMeasureSpec);
        Log.e("heigth -- width ",heigth+"  -- "+width);
        circleX=width/2;//初始化紅點座標位置
        circleY=20;
    }
 int[] colors={R.color.colorPrimary,R.color.colorAccent,R.color.color_environment_serious,R.color.color_environment_mild};
    @Override
    protected void onDraw(Canvas canvas) {

        super.onDraw(canvas);
        setProgColor(colors);
        //繪製一條線,線帽為半圓
        canvas.drawLine(50,20,width-50,20,paint);
        canvas.drawCircle(circleX,circleY,15,paintCircle2);
    }

到這裡,線和點就已經做好了,只是靜態的,下面是如何拖動,就要在onTouchEvent方法中去寫了,程式碼都已經添加了註釋  Math.abs(dhx)<50&&Math.abs(dhy)<50  是證明down的座標點和原始球的座標點相差範圍在50內,如果小球在螢幕200,200的位置,而我們手指down的點在800,800,那麼相差如此巨大,肯定不是我們想要的結果,所以,我們就認為down的座標減去球的座標差值最小(50內)才是我們想要的結果,這個時候我們再設定小球move的座標(讓小球跟隨手指移動)。

/**
     * 判斷所按壓的座標和紅點座標的關係
     * 如果手指按在了紅點的下方,那麼down-紅點y座標的絕對值如果等於或者小於半徑,也就是說目前按壓的就是紅點,
     *
     * */
    private boolean clickCircle(int downHeigth,int downWidth){
        int dhy = downHeigth - circleY;
        int dhx = downWidth - circleX;
        //證明按壓的是原點
        if(Math.abs(dhx)<50&&Math.abs(dhy)<50){
            return true;
        }
        return false;
    }
    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        return super.dispatchTouchEvent(event);
    }
    boolean isFlag=false;
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int rawX = (int) event.getX();
        int rawY = (int) event.getY();//獲取到檢視座標,想對於外部viewgroup來說。
        Log.e("raw",rawX+"  ---  "+rawY);
        Log.e("rawwww",circleX+"  ---  "+circleY);
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                //如果是手指down在了紅點上,move時可重繪
                if(clickCircle(rawY,rawX)) {
                    circleX = rawX;
                    isFlag=true;
                }else{
                    isFlag=false;
                }
                break;
            case MotionEvent.ACTION_MOVE:
                if(isFlag){
                    circleX = rawX;
                    invalidate();
                }
                break;
            case MotionEvent.ACTION_UP:
                isFlag=false;//抬起手指,狀態恢復
                break;
        }
        return true;//自己消費
    }

下一篇是圓形進