自定義View3-水波紋擴散(仿支付寶咻一咻)實現程式碼、思想

2022-08-29 18:02:57

PS:自定義view篇-水波紋實現

效果:水波紋擴散

場景:雷達、按鈕點選效果、搜尋等

實現:先上效果圖,之前記得支付寶有一個咻一咻,當時就是水波紋效果,實現起來一共兩步,第一畫內圓,第二畫多個外圓,不同時建立有間隔建立然後緩慢增大外圓半徑,到達最遠距離時移除掉,擴散時把透明度從255-1不斷賦值即可。複雜在第二步,開工。

 

 

 

 

 

 

 

開工

1、建立RippleView.class, 繼承與View

  • RippleView主要初始化一些資料,

  • onSizeChanged主要獲取位置座標

  • onDraw主要繪製影象,關鍵

public class RippleView extends View {

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

    public RippleView(Context context, @Nullable AttributeSet attrs) {
        //this(context, null, 0);//如果第二個引數寫null,則自定義屬性將不可用
        this(context, attrs, 0);
    }
  
    public RippleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        ........
    }    
   @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); ........ } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); ........ } }

 

  1.1特殊屬性解釋 

  •   alpha陣列:目的是讓每個外圓(擴散圓)透明度從不透明到透明(255-1)
  •   spreadRadius:擴散圓的半徑是遞增的
    private Paint centerPaint; //中心圓paint
    private int radius = 100; //中心圓半徑
    private Paint spreadPaint; //擴散圓paint
    private float centerX;//圓心x
    private float centerY;//圓心y
    private int distance = 5; //每次圓遞增間距
    private int maxRadius = 80; //最大圓半徑
    private int delayMilliseconds = 30;//擴散延遲間隔,越大擴散越慢
    private List<Integer> spreadRadius = new ArrayList<>();//擴散圓層級數,元素為擴散的距離
    private List<Integer> alphas = new ArrayList<>();//對應每層圓的透明度

  

  1.2新建attrs.xml檔案(res/values)

  我們需要在xml中使用自定義屬性來控制初始值,如內圓半徑,擴散顏色,內圓顏色等

<resources>

  <declare-styleable name="SpreadView">
    <!--中心圓顏色-->
    <attr name="spread_center_color" format="color" />
    <!--中心圓半徑-->
    <attr name="spread_radius" format="integer" />
    <!--擴散圓顏色-->
    <attr name="spread_spread_color" format="color" />
    <!--擴散間距-->
    <attr name="spread_distance" format="integer" />
    <!--擴散最大半徑-->
    <attr name="spread_max_radius" format="integer" />
    <!--擴散延遲間隔-->
    <attr name="spread_delay_milliseconds" format="integer" />
  </declare-styleable>

</resources>

   在RippleView中拿到值

 public RippleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.SpreadView, defStyleAttr, 0);
        radius = typedArray.getInt(R.styleable.SpreadView_spread_radius, radius);
        maxRadius = typedArray.getInt(R.styleable.SpreadView_spread_max_radius, maxRadius);
        int centerColor = typedArray.getColor(R.styleable.SpreadView_spread_center_color,
                ContextCompat.getColor(context, android.R.color.holo_red_light));
        int spreadColor = typedArray.getColor(R.styleable.SpreadView_spread_spread_color,
                ContextCompat.getColor(context, R.color.color_F71816));
        distance = typedArray.getInt(R.styleable.SpreadView_spread_distance, distance);
        typedArray.recycle();

    }

 context.obtainStyledAttributes可以獲取我們在xml檔案的屬性值,最後typedArray.recycle();釋放掉,為什麼釋放掉這也是一個學問,自行百度。

  • centerColor為內圓顏色,

  • spreadColor擴散顏色


  1.3初始化畫筆

public RippleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
       
        .....

        //中心圓
        centerPaint = new Paint();
        centerPaint.setColor(centerColor);
     //消除鋸齒 centerPaint.setAntiAlias(true); //水波紋擴散 spreadPaint = new Paint(); spreadPaint.setColor(spreadColor); spreadPaint.setAntiAlias(true);
     //填充和描邊,上面2張圖片效果不同取決於該屬性 spreadPaint.setStyle(Paint.Style.STROKE); spreadPaint.setAlpha(255); //初始化第一個水波紋,擴散半徑為0,透明度為255(不透明) spreadRadius.add(0); alphas.add(255); }
/**在控制元件大小發生改變時呼叫。所以這裡初始化會被呼叫一次*/
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
  super.onSizeChanged(w, h, oldw, oldh);
  //圓心位置
  centerX = w / 2;
   centerY = h / 2;
  }

 2、開始繪製onDraw()

  我們已經做了好前奏,剩下的就開始繪製了,首先我們要確定幾個圓才能形成水波紋效果,1,2還是3,不確定那就先從一個開始,spreadRadius我們在建立畫筆時已經新增了一個圓,那我們就遍歷spreadRadius陣列,透明度alphas[i]把值遞減(255-1),spreadRadius[i]圓半徑遞增,圓數量超過8個就移除第1個,如果最外圓擴散半徑達到最大半徑時新增新擴散圓。最後通過postInvalidateDelayed(30),延遲重新整理來達到擴散的樣式。

@Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        for (int i = 0; i < spreadRadius.size(); i++) {
            //透明度
            int alpha = alphas.get(i);
            //半徑
            int width = spreadRadius.get(i);
            spreadPaint.setAlpha(alpha);
            //繪製擴散的圓
            canvas.drawCircle(centerX, centerY, radius + width, spreadPaint);
            if (alpha > 0 && width < 255) {
                //遞減
                alpha = (alpha - distance) > 0 ? (alpha - distance) : 1;
                alphas.set(i, alpha);
                //遞增
                spreadRadius.set(i, width + distance);
            }
        }
        if (spreadRadius.get(spreadRadius.size() - 1) > maxRadius) {
            spreadRadius.add(0);
            alphas.add(255);
        }
        if (spreadRadius.size() > 8) {
            spreadRadius.remove(0);
            alphas.remove(0);
        }
        //中間的圓
        canvas.drawCircle(centerX, centerY, radius, centerPaint);
        //延遲更新,達到擴散視覺差效果
        postInvalidateDelayed(delayMilliseconds);
    }