Android自定義View——從零開始實現可暫停的旋轉動畫效果

Anlia發表於2017-12-22

版權宣告:本文為博主原創文章,未經博主允許不得轉載

系列教程:Android開發之從零開始系列

原始碼:本期內容比較簡單,原始碼就直接在文中貼出來了

大家要是看到有錯誤的地方或者有啥好的建議,歡迎留言評論

前言:大家平時有用過MAKA或者易企秀這些H5模板製作工具嗎,不知道里面有個小細節大家有沒注意到,就是這個音樂小控制元件

Android自定義View——從零開始實現可暫停的旋轉動畫效果

當我們點選這個控制元件時,它會開始旋轉並播放背景音樂,再次點選時會重置回初始狀態。類似的旋轉效果在APP中也十分常見,例如一些音樂播放介面中不斷旋轉的音樂碟片

Android自定義View——從零開始實現可暫停的旋轉動畫效果

其效果會更復雜一些,碟片會隨著音樂的播放、暫停而旋轉或暫停在某個旋轉角度,從暫停恢復到播放時,又會從當前的角度開始不斷地旋轉。本期將教大家如何利用 屬性動畫 ObjectAnimator補間動畫 RotateAnimation 分別實現這一效果

本篇只著重於思路和實現步驟,裡面用到的一些知識原理不會非常細地拿來講,如果有不清楚的api或方法可以在網上搜下相應的資料,肯定有大神講得非常清楚的,我這就不獻醜了。本著認真負責的精神我會把相關知識的博文連結也貼出來(其實就是懶不想寫那麼多哈哈),大家可以自行傳送。為了照顧第一次閱讀系列部落格的小夥伴,本篇有可能會出現一些在之前系列部落格就講過的內容,看過的童鞋自行跳過該段即可

國際慣例,先上效果圖

Android自定義View——從零開始實現可暫停的旋轉動畫效果


用ObjectAnimator實現

使用屬性動畫來實現這個效果是最簡單的,因為動畫的開始暫停結束重新播放等方法系統都已經為我們封裝好了(android 3.0以上開始支援),我們只需要繼承ImageView然後呼叫ObjectAnimator的相應方法即可,程式碼比較簡單,這裡就直接貼出來了

public class MusicButton extends AppCompatImageView {
    private ObjectAnimator objectAnimator;

    public static final int STATE_PLAYING =1;//正在播放
    public static final int STATE_PAUSE =2;//暫停
    public static final int STATE_STOP =3;//停止
    public int state;

    public MusicButton(Context context) {
        super(context);
        init();
    }

    public MusicButton(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public MusicButton(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init(){
        state = STATE_STOP;
        objectAnimator = ObjectAnimator.ofFloat(this, "rotation", 0f, 360f);//新增旋轉動畫,旋轉中心預設為控制元件中點
        objectAnimator.setDuration(3000);//設定動畫時間
        objectAnimator.setInterpolator(new LinearInterpolator());//動畫時間線性漸變
        objectAnimator.setRepeatCount(ObjectAnimator.INFINITE);
        objectAnimator.setRepeatMode(ObjectAnimator.RESTART);
    }

    public void playMusic(){
        if(state == STATE_STOP){
            objectAnimator.start();//動畫開始
            state = STATE_PLAYING;
        }else if(state == STATE_PAUSE){
            objectAnimator.resume();//動畫重新開始
            state = STATE_PLAYING;
        }else if(state == STATE_PLAYING){
            objectAnimator.pause();//動畫暫停
            state = STATE_PAUSE;
        }
    }

    public void stopMusic(){
        objectAnimator.end();//動畫結束
        state = STATE_STOP;
    }
}
複製程式碼

效果如圖

Android自定義View——從零開始實現可暫停的旋轉動畫效果


用RotateAnimation實現

RotateAnimation本身並沒有封裝暫停動畫的方法,所以實現起來會比ObjectAnimator要複雜一些,本著學習研究的態度,沒有困難創造困難也要上,我們就來分析一下如何使用“原始的”RotateAnimation實現我們想要的效果

因為RotateAnimation動畫只有開始和結束的方法,所以當我們點選暫停按鈕時,需要記錄當前已經旋轉的角度,重繪View並將畫布旋轉之前記錄的角度。再次播放時,因為View的畫布已經旋轉至之前暫停的角度,我們只需要新建一個動畫從當前角度播放即可,具體程式碼如下

public class MusicButton extends AppCompatImageView {
    public static final int STATE_PLAYING =1;//正在播放
    public static final int STATE_PAUSE =2;//暫停
    public static final int STATE_STOP =3;//停止
    public int state;

    private float angle;//記錄RotateAnimation中受插值器數值影響的角度
    private float angle2;//主要用來記錄暫停時停留的角度,即View初始旋轉角度
    private int viewWidth;
    private int viewHeight;
    private MusicAnim musicAnim;

    public MusicButton(Context context) {
        super(context);
        init();
    }

    public MusicButton(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public MusicButton(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init(){
        state = STATE_STOP;
        angle = 0;
        angle2 = 0;
        viewWidth = 0;
        viewHeight = 0;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        viewWidth = getMeasuredWidth();
        viewHeight = getMeasuredHeight();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.rotate(angle2,viewWidth/2,viewHeight/2);
        super.onDraw(canvas);
    }

    public class MusicAnim extends RotateAnimation{
        public MusicAnim(float fromDegrees, float toDegrees, float pivotX, float pivotY) {
            super(fromDegrees, toDegrees, pivotX, pivotY);
        }

        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            super.applyTransformation(interpolatedTime, t);
            angle = interpolatedTime * 360;
        }
    }

    public void playMusic(){
        if(state == STATE_PLAYING){
            angle2 = (angle2 + angle)%360;//可以取餘也可以不取,看實際的需求
            musicAnim.cancel();
            state = STATE_PAUSE;
            invalidate();
        }else {
            musicAnim = new MusicAnim(0,360,viewWidth/2,viewHeight/2);
            musicAnim.setDuration(3000);
            musicAnim.setInterpolator(new LinearInterpolator());//動畫時間線性漸變
            musicAnim.setRepeatCount(ObjectAnimator.INFINITE);
            startAnimation(musicAnim);
			state = STATE_PLAYING;
        }
    }

    public void stopMusic(){
        angle2 = 0;
        clearAnimation();
		state = STATE_STOP;
        invalidate();
    }
}
複製程式碼

MusicButton的程式碼寫完了,下面將按鈕結合音樂播放的程式碼貼出來,感興趣的小夥伴可以看看

mPlayer = MediaPlayer.create(this, R.raw.音樂名);
mPlayer.setLooping(true);

btnMusic = (MusicButton) findViewById(R.id.btn_music);
btnMusic.setOnClickListener(new View.OnClickListener() {//單擊播放或暫停
	@Override
	public void onClick(View v) {
		btnMusic.playMusic();
		try {
			if (mPlayer != null) {
				if (mPlayer.isPlaying()) {
					mPlayer.pause();
				} else {
					mPlayer.start();
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
});
btnMusic.setOnLongClickListener(new View.OnLongClickListener() {//長按停止
	@Override
	public boolean onLongClick(View v) {
		try {
			if (mPlayer != null) {
				mPlayer.stop();
				mPlayer.prepare();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		btnMusic.stopMusic();
		return true;//消費此長按事件,不再向下傳遞
	}
});
複製程式碼

至此本篇教程到此結束,如果大家看了感覺還不錯麻煩點個贊,你們的支援是我最大的動力~


相關文章