1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

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

        共 6169字,需瀏覽 13分鐘

         ·

        2021-04-08 10:24

        效果圖:



        一、思路分析


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


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


        二、功能實現(xiàn)


        1.首先通過貝塞爾曲線實現(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:為水波水平方向移動的距離。waveLength:為水波長度,一個上弧加一個下弧為一個波長。halfWaveLength:為半個水波長度。

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



        利用屬性動畫來不斷的改變path起始點的x值,使水波水平移動

        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)點,這里可以通過PathMeasure來得到。


        PathMeasure有個getMatrix方法,通過這個方法可以獲取指定長度的位置坐標(biāo)及該點Matrix。先來看下這個方法:

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

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

        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起點的長度。
        我們發(fā)現(xiàn)小船在沿著水波移動的時候,會根據(jù)波浪的高低傾斜


        上面提到getMatrix方法,不僅返回指定長度的位置坐標(biāo),還會返回該點的matrix,這個時候就需要用到返回的matrix,使用matrix的preTranslate方法來實現(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);}


        至此我們實現(xiàn)了小船的繪制,但是小船并沒有移動起來,我們還需要利用屬性動畫來使小船移動起來

        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;    // 水波長度  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); }
        /** * 平移動畫 */ 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
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            懂色影院 | 熟女少妇内射日韩亚洲 | 99久久精品免费看国产交换 | 中国日逼视频 | 逼逼逼逼逼逼 | 囯产精品久久久久久久久在饯观看 | 人人色网站 | 亚洲色图偷偷撸 | 亚洲乱论视频 | 女人双腿扒开让男人桶 |