<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Android自定義實(shí)現(xiàn)乘風(fēng)破浪的小船

          共 6169字,需瀏覽 13分鐘

           ·

          2021-04-08 10:24

          效果圖:



          一、思路分析


          整個(gè)效果分為兩部分,第一部分是波浪形的水波,第二部分是小船沿著水波移動(dòng),并且水波是和小船向著相反的方向移動(dòng)的。


          水波我們可以通過(guò)貝塞爾曲線來(lái)實(shí)現(xiàn),小船沿著水波移動(dòng)的效果通過(guò)PathMeasure來(lái)實(shí)現(xiàn),然后使用屬性動(dòng)畫讓水波和小船動(dòng)起來(lái)。


          二、功能實(shí)現(xiàn)


          1.首先通過(guò)貝塞爾曲線實(shí)現(xiàn)水波

          private void drawWave(Canvas canvas){    mPath.reset();    mPath.moveTo(0 - mDeltaX, mHeight / 2);    for (int i = 0; i <= getWidth() + waveLength; i += waveLength) {        mPath.rQuadTo(halfWaveLength / 2, waveHeight, halfWaveLength, 0);        mPath.rQuadTo(halfWaveLength / 2, -waveHeight, halfWaveLength, 0);    }    mPath.lineTo(getWidth() + waveLength, getHeight());    mPath.lineTo(0, getHeight());    mPath.close();    canvas.drawPath(mPath, mPaint);}
          mDeltaX:為水波水平方向移動(dòng)的距離。waveLength:為水波長(zhǎng)度,一個(gè)上弧加一個(gè)下弧為一個(gè)波長(zhǎng)。halfWaveLength:為半個(gè)水波長(zhǎng)度。

          先把path的起始點(diǎn)移動(dòng)到屏幕的一半高度的位置,然后循環(huán)畫曲線,長(zhǎng)度為屏幕的寬度加上一個(gè)波長(zhǎng),然后連接到整個(gè)屏幕高度的位置,最后形成一個(gè)封閉的區(qū)域,這樣就實(shí)現(xiàn)了一個(gè)填充的水波效果。



          利用屬性動(dòng)畫來(lái)不斷的改變path起始點(diǎn)的x值,使水波水平移動(dòng)

          private void startAnim(){    ValueAnimator animator = ValueAnimator.ofFloat(0, 1);    animator.addUpdateListener(animation -> {        mDeltaX = waveLength * ((float) animation.getAnimatedValue());        postInvalidate();    });    animator.setDuration(13000);    animator.setInterpolator(new LinearInterpolator());    animator.setRepeatCount(ValueAnimator.INFINITE);    animator.start();}



          2、接著就是把小船給繪制到水波上,要繪制小船,得先知道要繪制到哪里,也就是要知道繪制的坐標(biāo)點(diǎn),這里可以通過(guò)PathMeasure來(lái)得到。


          PathMeasure有個(gè)getMatrix方法,通過(guò)這個(gè)方法可以獲取指定長(zhǎng)度的位置坐標(biāo)及該點(diǎn)Matrix。先來(lái)看下這個(gè)方法:

          boolean getMatrix (float distance, Matrix matrix, int flags)
          distance:距離Path起點(diǎn)的長(zhǎng)度matrix:根據(jù)flags封裝好的矩陣flags:選擇哪些內(nèi)容會(huì)傳入到matrix中,可選值有POSITION_MATRIX_FLAG(位置)和ANGENT_MATRIX_FLAG(正切)。

          有了這個(gè)方法,我們就可以繪制小船了

          private void drawBitmap(Canvas canvas) {    mPathMeasure.setPath(mPath, false);    mMatrix.reset();    mPathMeasure.getMatrix(mDistance, mMatrix, PathMeasure.TANGENT_MATRIX_FLAG | PathMeasure.POSITION_MATRIX_FLAG);    canvas.drawBitmap(mBitMap,mMatrix,mPaint);}


          mDistance就是距離Path起點(diǎn)的長(zhǎng)度。
          我們發(fā)現(xiàn)小船在沿著水波移動(dòng)的時(shí)候,會(huì)根據(jù)波浪的高低傾斜


          上面提到getMatrix方法,不僅返回指定長(zhǎng)度的位置坐標(biāo),還會(huì)返回該點(diǎn)的matrix,這個(gè)時(shí)候就需要用到返回的matrix,使用matrix的preTranslate方法來(lái)實(shí)現(xiàn)小船角度的傾斜。

          private void drawBitmap(Canvas canvas) {    mPathMeasure.setPath(mPath, false);    mMatrix.reset();    mPathMeasure.getMatrix(mDistance, mMatrix, PathMeasure.TANGENT_MATRIX_FLAG | PathMeasure.POSITION_MATRIX_FLAG);    mMatrix.preTranslate(- mBitMap.getWidth() / 2, - mBitMap.getHeight());    canvas.drawBitmap(mBitMap,mMatrix,mPaint);}


          至此我們實(shí)現(xiàn)了小船的繪制,但是小船并沒(méi)有移動(dòng)起來(lái),我們還需要利用屬性動(dòng)畫來(lái)使小船移動(dòng)起來(lái)

          private void startAnim(){    ValueAnimator animator = ValueAnimator.ofFloat(0, 1);    animator.addUpdateListener(animation -> {        mDeltaX = waveLength * ((float) animation.getAnimatedValue());
          mDistance = (getWidth() + waveLength + halfWaveLength) * ((float)animation.getAnimatedValue()); postInvalidate(); }); animator.setDuration(13000); animator.setInterpolator(new LinearInterpolator()); animator.setRepeatCount(ValueAnimator.INFINITE); animator.start();}


          貼上完整代碼:

          public class WaveView extends View {    private Paint mPaint;    private Path mPath;    // 水波長(zhǎng)度  private int waveLength = 800;    // 水波高度  private int waveHeight = 150;    private int mHeight;    private int halfWaveLength = waveLength / 2;    private float mDeltaX;    private Bitmap mBitMap;    private PathMeasure mPathMeasure;    private Matrix mMatrix;    private float mDistance;
          public WaveView(Context context) { this(context, null); }
          public WaveView(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); }
          public WaveView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { this(context, attrs, defStyleAttr, 0); }
          public WaveView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); init(); }
          private void init(){ mPaint = new Paint(); mPaint.setColor(getResources().getColor(R.color.sea_blue)); mPaint.setStyle(Paint.Style.FILL); mPaint.setAntiAlias(true);
          mPath = new Path();
          mMatrix = new Matrix(); mPathMeasure = new PathMeasure();
          Options opts = new Options(); opts.inSampleSize = 3; mBitMap = BitmapFactory.decodeResource(getResources(), R.drawable.ship, opts); }
          @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); mHeight = h;
          startAnim(); }
          @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); drawWave(canvas);
          drawBitmap(canvas); }
          /** * 繪制水波 * @param canvas */ private void drawWave(Canvas canvas){ mPath.reset(); mPath.moveTo(0 - mDeltaX, mHeight / 2); for (int i = 0; i <= getWidth() + waveLength; i += waveLength) { mPath.rQuadTo(halfWaveLength / 2, waveHeight, halfWaveLength, 0); mPath.rQuadTo(halfWaveLength / 2, -waveHeight, halfWaveLength, 0); } mPath.lineTo(getWidth() + waveLength, getHeight()); mPath.lineTo(0, getHeight()); mPath.close();
          canvas.drawPath(mPath, mPaint); }
          /** * 繪制小船 * @param canvas */ private void drawBitmap(Canvas canvas) { mPathMeasure.setPath(mPath, false); mMatrix.reset(); mPathMeasure.getMatrix(mDistance, mMatrix, PathMeasure.TANGENT_MATRIX_FLAG | PathMeasure.POSITION_MATRIX_FLAG); mMatrix.preTranslate(- mBitMap.getWidth() / 2, - mBitMap.getHeight()); canvas.drawBitmap(mBitMap,mMatrix,mPaint); }
          /** * 平移動(dòng)畫 */ private void startAnim(){ ValueAnimator animator = ValueAnimator.ofFloat(0, 1); animator.addUpdateListener(animation -> { mDeltaX = waveLength * ((float) animation.getAnimatedValue());
          mDistance = (getWidth() + waveLength + halfWaveLength) * ((float)animation.getAnimatedValue()); postInvalidate(); }); animator.setDuration(13000); animator.setInterpolator(new LinearInterpolator()); animator.setRepeatCount(ValueAnimator.INFINITE); animator.start(); }}


          源碼地址:

          https://github.com/loren325/CustomerView


          到這里就結(jié)束啦.


          瀏覽 94
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  黄色A片播放| 人妻三级视频 | 免费看片18禁 | 操操操操操网 | 欧美成人在线18岁勿进 |