一、自定义属性
在res目录下添加一个XML文件attrs.xml
分析我们需要那些自定义的属性
一个进度条,首先我们需要一个LineHeight代表我们进度条的高度,其次是已完成的进度条的颜色与未完成的进度条的颜色, 还有就是一个标记进度条位置的圆点或者是其他的一个标记(这里我使用的是圆形,所以我们还需要它的半径)。当然大家可以根据自己的需求去设置更多的自定义属性。
这里先声明自定义属性:
<attr name="play_color" format="color" /> <attr name="line_height" format="dimension" /> <attr name="un_play_color" format="color" /> <attr name="circle_radius" format="dimension" />
接着引用我们的自定义属性:
注意:这里的名称是需要我们先创建我们自定义控件的名字并且与它一致
<declare-styleable name="MusicProgressbar"> <attr name="line_height" /> <attr name="play_color" /> <attr name="un_play_color" /> <attr name="circle_radius" /> </declare-styleable>
接着我们要在我们的自定义控件的构造函数里去获取它的自定义属性
为了保证我们的自定义控件能够正常的运行,我们需要去给它设置默认的值(因为我们都比较懒,如果不设置默认的值,我们需要把我们自定义的值全部设置才能使用,这样是不行的,因为我们并不想去设置这么多值,很麻烦,对不对?!!主要的懒!同时也不符合规范!例如:我们的系统控件,TextView我们不设置它的字体颜色,同样也能显示,是因为它给我们设置了默认的颜色),说的太多了,这里主要是为了新手能够更好的去理解,大神们就呵呵一些吧!O(∩_∩)O~
private static final int DEFAULT_LINE_HEIGHT = 1;//DP private static final int DEFAULT_PLAY_COLOR = 0XFFFC00D; private static final int DEFAULT_UN_PLAY_COLOR = 0XFFD3D6DA; private static final int DEFAULT_CIRCLE_RADIUS = 2;//DP private int mLineHeight = dpTopx(DEFAULT_LINE_HEIGHT); private int mPlayColor = DEFAULT_PLAY_COLOR; private int mUnPlayColor = DEFAULT_UN_PLAY_COLOR; private int mCircleRadius = dpTopx(DEFAULT_CIRCLE_RADIUS); private Paint mPaint = new Paint();
public MusicProgressbar(Context context) { this(context, null); } public MusicProgressbar(Context context, AttributeSet attrs) { this(context, attrs, 0); } public MusicProgressbar(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); //获取我们的自定义属性 getStyledAttrs(attrs); }
private void getStyledAttrs(AttributeSet attrs) { TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.MusicProgressbar); mLineHeight = (int) typedArray.getDimension (R.styleable.MusicProgressbar_line_height, mLineHeight); mPlayColor = typedArray.getColor (R.styleable.MusicProgressbar_play_color, mPlayColor); mUnPlayColor = typedArray.getColor (R.styleable.MusicProgressbar_un_play_color, mUnPlayColor); mCircleRadius = (int) typedArray.getDimension (R.styleable.MusicProgressbar_circle_radius, mCircleRadius); //设置抗锯齿 mPaint.setAntiAlias(true); typedArray.recycle(); }
二、测量onMeasure
测量有三种模式,由于个人习惯一般会设置具体的值,所以这里没有去进行测量,大家可以根据自己的需求去进行测量!!!
@Override protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int width = MeasureSpec.getSize(widthMeasureSpec); int height = MeasureSpec.getSize(heightMeasureSpec); setMeasuredDimension(width, height); }
三、绘制onDraw
这里的注释写的很多了,大家如果认真去看,相信应该
@Override protected synchronized void onDraw(Canvas canvas) { canvas.save(); //确定我们的绘制区域 canvas.translate(getPaddingLeft(), getHeight() / 2); //是否需要去绘制加载的进度条 boolean noNeedPlay = false; //进度条肯定有一个当前的进度,与一 个最大的进度 float radio = getProgress() * 1.0f / getMax(); float progressX = radio * getWidth(); int circleWidth = 2 * mCircleRadius; if (progressX + circleWidth > getWidth()) { progressX = getWidth() - circleWidth; noNeedPlay = true; } mPaint.setColor(mPlayColor); canvas.drawCircle(progressX + mCircleRadius, 0, mCircleRadius, mPaint); if (progressX > 0) { mPaint.setColor(mPlayColor); //设置线宽 mPaint.setStrokeWidth(mLineHeight); canvas.drawLine(0, 0, progressX, 0, mPaint); } if (!noNeedPlay) { float start = progressX; mPaint.setColor(mUnPlayColor); //设置线宽 mPaint.setStrokeWidth(mLineHeight); canvas.drawLine(progressX + circleWidth, 0, getWidth(), 0, mPaint); } canvas.restore(); }
四、因为我们的继承的ProgressBar所以我们不必去考虑状态保存的问题,系统都已经跟我们设置好了,所以不必去考虑。如果我们经常的是的View那么我们就需要去考虑了!这里直接把完整代码献上!
public class MusicProgressbar extends ProgressBar { private static final int DEFAULT_LINE_HEIGHT = 1;//DP private static final int DEFAULT_PLAY_COLOR = 0XFFFC00D; private static final int DEFAULT_UN_PLAY_COLOR = 0XFFD3D6DA; private static final int DEFAULT_CIRCLE_RADIUS = 2;//DP private int mLineHeight = dpTopx(DEFAULT_LINE_HEIGHT); private int mPlayColor = DEFAULT_PLAY_COLOR; private int mUnPlayColor = DEFAULT_UN_PLAY_COLOR; private int mCircleRadius = dpTopx(DEFAULT_CIRCLE_RADIUS); private Paint mPaint = new Paint(); public MusicProgressbar(Context context) { this(context, null); } public MusicProgressbar(Context context, AttributeSet attrs) { this(context, attrs, 0); } public MusicProgressbar(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); //获取我们的自定义属性 getStyledAttrs(attrs); } private void getStyledAttrs(AttributeSet attrs) { TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.MusicProgressbar); mLineHeight = (int) typedArray.getDimension (R.styleable.MusicProgressbar_line_height, mLineHeight); mPlayColor = typedArray.getColor (R.styleable.MusicProgressbar_play_color, mPlayColor); mUnPlayColor = typedArray.getColor (R.styleable.MusicProgressbar_un_play_color, mUnPlayColor); mCircleRadius = (int) typedArray.getDimension (R.styleable.MusicProgressbar_circle_radius, mCircleRadius); //设置抗锯齿 mPaint.setAntiAlias(true); typedArray.recycle(); } @Override protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int width = MeasureSpec.getSize(widthMeasureSpec); int height = MeasureSpec.getSize(heightMeasureSpec); setMeasuredDimension(width, height); } @Override protected synchronized void onDraw(Canvas canvas) { canvas.save(); //确定我们的绘制区域 canvas.translate(getPaddingLeft(), getHeight() / 2); //是否需要去绘制加载的进度条 boolean noNeedPlay = false; //进度条肯定有一个当前的进度,与一 个最大的进度 float radio = getProgress() * 1.0f / getMax(); float progressX = radio * getWidth(); int circleWidth = 2 * mCircleRadius; if (progressX + circleWidth > getWidth()) { progressX = getWidth() - circleWidth; noNeedPlay = true; } mPaint.setColor(mPlayColor); canvas.drawCircle(progressX + mCircleRadius, 0, mCircleRadius, mPaint); if (progressX > 0) { mPaint.setColor(mPlayColor); //设置线宽 mPaint.setStrokeWidth(mLineHeight); canvas.drawLine(0, 0, progressX, 0, mPaint); } if (!noNeedPlay) { float start = progressX; mPaint.setColor(mUnPlayColor); //设置线宽 mPaint.setStrokeWidth(mLineHeight); canvas.drawLine(progressX + circleWidth, 0, getWidth(), 0, mPaint); } canvas.restore(); } /** * dp转px * * @param dpVal dp的值 * @return px的值 */ private int dpTopx(int dpVal) { return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpVal, getResources().getDisplayMetrics()); } }
到这里我们的自定义音乐播放进度条就完成了
五、使用
写了这么多,终于要到了使用的时刻了,是不是有点小激动呢?!
我们如何在xml中去使用我们的自定义属性呢?这一步就很关键了!
xmlns:ghost="http://schemas.android.com/apk/res/com.ghost.newprogressbar"
这里我们需要加入我们的命名空间
前面的名字,我们可以自己去随便去取,这个没关系,中间一部分是不是很熟悉呢?!嗯!?
最关键的是我们最后面的了,是我们项目的包名,小伙伴们记得别搞错了哦!
此时我们就可以在xml中去添加我们的自定义属性了!
<LinearLayout android:layout_width="match_parent" android:layout_height="20dp" android:layout_marginTop="30dp" android:orientation="horizontal"> <TextView android:id="@+id/tv_play" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_marginLeft="10dp" android:layout_marginRight="10dp" android:gravity="center" android:text="00:00" android:textSize="15sp" /> <com.ghost.newprogressbar.MusicProgressbar android:id="@+id/play_progressbar" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" ghost:circle_radius="10dp" ghost:line_height="4dp" ghost:play_color="#e6de10" /> <TextView android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_marginLeft="10dp" android:layout_marginRight="10dp" android:gravity="center" android:text="03:30" android:textSize="15sp" /> </LinearLayout>
接着我们就要在我们的activity中去使用了!说了这么多,也不想再说了!有点懒了!小伙伴见谅哦!O(∩_∩)O哈哈~
既然你们都原谅了,那我就不客气了哦!直接上代码了!
public class MainActivity extends AppCompatActivity { private MusicProgressbar musicProgressbar; private TextView tvPlay; private Button btnPlay; private int count = 0; private static final int MSG_PLAY = 0x120; private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); ++count; //获得我们当前进度 int progress = count * 100 / 210; musicProgressbar.setProgress(progress); tvPlay.setText(sToM(count)); if (progress >= 100) { mHandler.removeMessages(MSG_PLAY); } else { mHandler.sendEmptyMessageDelayed(MSG_PLAY, 1000); } } }; //秒转为分 private String sToM(int count) { int m = count / 60; int s = count % 60; if (m <= 9) { if (s <= 9) { return "0" + m + ":0" + s; } else { return "0" + m + ":" + s; } } else { if (s <= 9) { return m + ":0" + s; } else { return m + ":" + s; } } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //初始化控件 initView(); mHandler.sendEmptyMessage(MSG_PLAY); } int isPlay = 0; private void initView() { musicProgressbar = findViewById(R.id.play_progressbar); tvPlay = findViewById(R.id.tv_play); btnPlay = findViewById(R.id.btn_play); btnPlay.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { isPlay = isPlay % 2; if (isPlay == 0) { //暂停播放 mHandler.removeMessages(MSG_PLAY); btnPlay.setBackground(getResources().getDrawable(R.drawable.ic_play_arrow_black_30dp)); } else { //正在播放 mHandler.sendEmptyMessage(MSG_PLAY); btnPlay.setBackground(getResources().getDrawable(R.drawable.ic_pause_black_30dp)); } isPlay++; } }); } }
我这里传入的是具体的值,3分30秒的一个模拟播放,没有实际的去添加音乐进行播放,小伙伴原谅我吧,我比较懒!(大家千万不要学我!)
因为新买的电脑没装QQ,所以没有截取效果图,尴尬O(∩_∩)O~
等有空,我很快就会加上的,对不住了,各位朋友!!!原谅我把!谢谢大家能看到这里,万分感谢,欢迎留言!!!让我们携手进步吧!