目錄

效果
國際慣例,先放效果

前言
之前寫了java線程的暫停和繼續(xù),Android下載暫停,圓形下載進(jìn)度條,相信大家看了上面的動畫效果就知道我這一個(gè)系列是要做什么了,哈哈,沒錯(cuò)就是一個(gè)簡單的網(wǎng)絡(luò)下載暫停,只是通過一部分一部分的分析,今天把動畫給搞定了,過段時(shí)間就是可以成功的把這些整合起來,預(yù)計(jì)效果就是我們手機(jī)應(yīng)用商城中的軟件下載的效果,可以暫停,也是圓形進(jìn)度條;擁有開始和暫停的動畫;想想就非常不錯(cuò);666666666
正文
自定義View PlayPauseView
上面的動畫就是兩個(gè)矩形,變成三角形,我們只用設(shè)置左邊矩形的四個(gè)點(diǎn)的坐標(biāo),那么右邊的坐標(biāo),可以通過自己定義的兩個(gè)矩形之間的寬度,算出相應(yīng)的坐標(biāo),
Path方法就是用畫筆畫出相應(yīng)的動畫效果,path.moveTo(),就是從起點(diǎn)畫到后面的點(diǎn),path.lineTo()畫直線,通過兩個(gè)點(diǎn)確定一個(gè)直線,就可以成功的畫出動畫,path.close()這可不是關(guān)閉方法,而是把線段封閉掉,什么意思呢?我們花了四個(gè)點(diǎn),但是只連接了三個(gè)線,那么就會把開始的點(diǎn)和最后的點(diǎn)連接起來,這樣就形成了矩形。
public class PlayPauseView extends View {
private int mWidth; //View寬度
private int mHeight; //View高度
private Paint mPaint;
private Path mLeftPath; //暫停時(shí)左側(cè)豎條Path
private Path mRightPath; //暫停時(shí)右側(cè)豎條Path
private float mGapWidth; //兩個(gè)暫停豎條中間的空隙,默認(rèn)為兩側(cè)豎條的寬度
private float mProgress; //動畫Progress
private Rect mRect;
private boolean isPlaying;
private float mRectWidth; //圓內(nèi)矩形寬度
private float mRectHeight; //圓內(nèi)矩形高度
private float mRectLT; //矩形左側(cè)上側(cè)坐標(biāo)
private float mRadius; //圓的半徑
private int mBgColor = Color.WHITE;
private int mBtnColor = Color.BLACK;
private int mDirection = Direction.POSITIVE.value;
private float mPadding;
private int mAnimDuration = 200;//動畫時(shí)間
public PlayPauseView(Context context) {
super(context);
}
public PlayPauseView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public PlayPauseView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
mPaint = new Paint();
mPaint.setAntiAlias(true);
mLeftPath = new Path();
mRightPath = new Path();
mRect = new Rect();
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.PlayPauseView);
mBgColor = ta.getColor(R.styleable.PlayPauseView_bg_color, Color.WHITE);
mBtnColor = ta.getColor(R.styleable.PlayPauseView_btn_color, Color.BLACK);
mGapWidth = ta.getDimensionPixelSize(R.styleable.PlayPauseView_gap_width, dp2px(context, 0));
mPadding = ta.getDimensionPixelSize(R.styleable.PlayPauseView_space_padding, dp2px(context, 0));
mDirection = ta.getInt(R.styleable.PlayPauseView_anim_direction, Direction.POSITIVE.value);
mAnimDuration = ta.getInt(R.styleable.PlayPauseView_anim_duration, 200);
ta.recycle();
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mWidth = MeasureSpec.getSize(widthMeasureSpec);
mHeight = MeasureSpec.getSize(heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
if (widthMode == MeasureSpec.EXACTLY) {
mWidth = Math.min(mWidth, mHeight);
} else {
mWidth = dp2px(getContext(), 50);
}
if (heightMode == MeasureSpec.EXACTLY) {
mHeight = Math.min(mWidth, mHeight);
} else {
mHeight = dp2px(getContext(), 50);
}
mWidth = mHeight = Math.min(mWidth, mHeight);
setMeasuredDimension(mWidth, mHeight);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mWidth = mHeight = w;
initValue();
}
private void initValue() {
mRadius = mWidth / 2;
mPadding = getSpacePadding() == 0 ? mRadius / 3f : getSpacePadding();
if (getSpacePadding() > mRadius / Math.sqrt(2) || mPadding < 0) {
mPadding = mRadius / 3f; //默認(rèn)值
}
float space = (float) (mRadius / Math.sqrt(2) - mPadding); //矩形寬高的一半
mRectLT = mRadius - space;
float rectRB = mRadius + space;
mRectWidth = 2 * space;
mRectHeight = 2 * space;
mGapWidth = getGapWidth() != 0 ? getGapWidth() : mRectWidth / 3;
mProgress = isPlaying ? 0 : 1;
mAnimDuration = getAnimDuration() < 0 ? 200 : getAnimDuration();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mLeftPath.rewind();
mRightPath.rewind();
mPaint.setColor(mBgColor);
canvas.drawCircle(mWidth / 2, mHeight / 2, mRadius, mPaint);
float distance = mGapWidth * (1 - mProgress); //暫停時(shí)左右兩邊矩形距離
float barWidth = mRectWidth / 2 - distance / 2; //一個(gè)矩形的寬度
float leftLeftTop = barWidth * mProgress; //左邊矩形左上角
float rightLeftTop = barWidth + distance; //右邊矩形左上角
float rightRightTop = 2 * barWidth + distance; //右邊矩形右上角
float rightRightBottom = rightRightTop - barWidth * mProgress; //右邊矩形右下角
mPaint.setColor(mBtnColor);
mPaint.setStyle(Paint.Style.FILL);
if (mDirection == Direction.NEGATIVE.value) {
mLeftPath.moveTo(mRectLT, mRectLT);
mLeftPath.lineTo(leftLeftTop + mRectLT, mRectHeight + mRectLT);
mLeftPath.lineTo(barWidth + mRectLT, mRectHeight + mRectLT);
mLeftPath.lineTo(barWidth + mRectLT, mRectLT);
mLeftPath.close();
mRightPath.moveTo(rightLeftTop + mRectLT, mRectLT);
mRightPath.lineTo(rightLeftTop + mRectLT, mRectHeight + mRectLT);
mRightPath.lineTo(rightRightBottom + mRectLT, mRectHeight + mRectLT);
mRightPath.lineTo(rightRightTop + mRectLT, mRectLT);
mRightPath.close();
} else {
mLeftPath.moveTo(leftLeftTop + mRectLT, mRectLT);
mLeftPath.lineTo(mRectLT, mRectHeight + mRectLT);
mLeftPath.lineTo(barWidth + mRectLT, mRectHeight + mRectLT);
mLeftPath.lineTo(barWidth + mRectLT, mRectLT);
mLeftPath.close();
mRightPath.moveTo(rightLeftTop + mRectLT, mRectLT);
mRightPath.lineTo(rightLeftTop + mRectLT, mRectHeight + mRectLT);
mRightPath.lineTo(rightLeftTop + mRectLT + barWidth, mRectHeight + mRectLT);
mRightPath.lineTo(rightRightBottom + mRectLT, mRectLT);
mRightPath.close();
}
canvas.save();
canvas.translate(mRectHeight / 8f * mProgress, 0);
float progress = isPlaying ? (1 - mProgress) : mProgress;
int corner = mDirection == Direction.NEGATIVE.value ? -90 : 90;
float rotation = isPlaying ? corner * (1 + progress) : corner * progress;
canvas.rotate(rotation, mWidth / 2f, mHeight / 2f);
canvas.drawPath(mLeftPath, mPaint);
canvas.drawPath(mRightPath, mPaint);
canvas.restore();
}
public ValueAnimator getPlayPauseAnim() {
ValueAnimator valueAnimator = ValueAnimator.ofFloat(isPlaying ? 1 : 0, isPlaying ? 0 : 1);
valueAnimator.setDuration(mAnimDuration);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mProgress = (float) animation.getAnimatedValue();
invalidate();
}
});
return valueAnimator;
}
public void play() {
if (getPlayPauseAnim() != null) {
getPlayPauseAnim().cancel();
}
setPlaying(true);
getPlayPauseAnim().start();
}
public void pause() {
if (getPlayPauseAnim() != null) {
getPlayPauseAnim().cancel();
}
setPlaying(false);
getPlayPauseAnim().start();
}
private PlayPauseListener mPlayPauseListener;
public void setPlayPauseListener(PlayPauseListener playPauseListener) {
mPlayPauseListener = playPauseListener;
setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (isPlaying()) {
pause();
if (null != mPlayPauseListener) {
mPlayPauseListener.pause();
}
} else {
play();
if (null != mPlayPauseListener) {
mPlayPauseListener.play();
}
}
}
});
}
public interface PlayPauseListener {
void play();
void pause();
}
public int dp2px(Context context, float dpVal) {
float density = context.getResources().getDisplayMetrics().density;
return (int) (density * dpVal + 0.5f);
}
/* ------------下方是參數(shù)------------- */
public boolean isPlaying() {
return isPlaying;
}
public void setPlaying(boolean playing) {
isPlaying = playing;
}
public void setGapWidth(float gapWidth) {
mGapWidth = gapWidth;
}
public float getGapWidth() {
return mGapWidth;
}
public int getBgColor() {
return mBgColor;
}
public int getBtnColor() {
return mBtnColor;
}
public int getDirection() {
return mDirection;
}
public void setBgColor(int bgColor) {
mBgColor = bgColor;
}
public void setBtnColor(int btnColor) {
mBtnColor = btnColor;
}
public void setDirection(Direction direction) {
mDirection = direction.value;
}
public float getSpacePadding() {
return mPadding;
}
public void setSpacePadding(float padding) {
mPadding = padding;
}
public int getAnimDuration() {
return mAnimDuration;
}
public void setAnimDuration(int animDuration) {
mAnimDuration = animDuration;
}
public enum Direction {
POSITIVE(1),//順時(shí)針
NEGATIVE(2);//逆時(shí)針
int value;
Direction(int value) {
this.value = value;
}
}
}
mBgColor = ta.getColor(R.styleable.PlayPauseView_bg_color, Color.WHITE);
mBtnColor = ta.getColor(R.styleable.PlayPauseView_btn_color, Color.BLACK);
mGapWidth = ta.getDimensionPixelSize(R.styleable.PlayPauseView_gap_width, dp2px(context, 0));
mPadding = ta.getDimensionPixelSize(R.styleable.PlayPauseView_space_padding, dp2px(context, 0));
mDirection = ta.getInt(R.styleable.PlayPauseView_anim_direction, Direction.POSITIVE.value);
mAnimDuration = ta.getInt(R.styleable.PlayPauseView_anim_duration, 200);
大家可能不太清楚這些是什么,其實(shí)這個(gè)就是我們在xml中聲名的屬性,也就是自定義View的屬性;
自定義xml attrs.xml
<resources>
<declare-styleable name="PlayPauseView">
<attr name="bg_color" format="color"/>
<attr name="btn_color" format="color"/>
<attr name="gap_width" format="dimension|reference"/>
<attr name="space_padding" format="dimension|reference"/>
<attr name="anim_duration" format="integer"/>
<attr name="anim_direction">
<enum name="positive" value="1"/>
<enum name="negative" value="2"/>
</attr>
</declare-styleable>
</resources>
大家看到上面的name,就是我們在自定義View 中的屬性方法;
通過相應(yīng)的設(shè)置,大家就可以看出來了;
界面 activity_main.xml
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.freedom.lauzy.playpauseview.MainActivity">
<com.freedom.lauzy.playpauseviewlib.PlayPauseView
android:id="@+id/play_pause_view1"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_gravity="center"
android:layout_marginTop="40dp"
app:anim_direction="positive"
app:anim_duration="300"
app:bg_color="#E0E0E0"
app:btn_color="#000000"/>
</LinearLayout>
看見上面的app:后下面跟的屬性名稱了嗎?就是我們自定義View 的那些屬性,我們是在自定義的時(shí)候通過get()``set()方法,設(shè)置屬性的;
主方法 MainActivity()
package com.freedom.lauzy.playpauseview;
import android.graphics.Color;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Toast;
import com.freedom.lauzy.playpauseviewlib.PlayPauseView;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final PlayPauseView playPauseView1 = (PlayPauseView) findViewById(R.id.play_pause_view1);
playPauseView1.setPlayPauseListener(new PlayPauseView.PlayPauseListener() {
@Override
public void play() {
Toast.makeText(MainActivity.this, "Play", Toast.LENGTH_SHORT).show();
}
@Override
public void pause() {
Toast.makeText(MainActivity.this, "Pause", Toast.LENGTH_SHORT).show();
}
});
}
}
這個(gè)就還是蠻簡單的,設(shè)置一下點(diǎn)擊事件就可以了,我們自定義的時(shí)候,自定義了點(diǎn)擊事件(可以看看上面的代碼)。
總結(jié)
自定義View時(shí),要想一想我們自定義需要什么功能,他有動畫嗎,需要什么樣的參數(shù),可設(shè)置的屬性有需要多少,需要?jiǎng)赢媶??需要什么要的動畫,等?/p>
- 首先就是兩個(gè)矩形,先考慮如果畫出兩個(gè)矩形,先畫出一個(gè)圓,配置該視圖的寬高,就像畫出個(gè)圓形,在圓形里面畫出矩形,左邊矩形的坐標(biāo),如果畫,兩個(gè)矩形的寬度設(shè)置出來,就知道相應(yīng)的右邊的坐標(biāo),這樣子兩個(gè)矩形就出來了
- 其次就是個(gè)三角形,也就是三個(gè)點(diǎn)的坐標(biāo),通過寬度換算成適合的大小,畫出即可;
- 最后就是動畫,設(shè)置動畫時(shí)間,動畫效果
- 最后就是可配置的參數(shù)和屬性;
感謝
感謝大家?。。?加油?。?! 努力??! 日更!!