知识点:自定义控件,MediaPlayer,Ripple水波纹效果,CardView
自己计划的app在github上面很久了,一直没有什么进展,后来发现是自己想多了,计划太多感觉好难啊以后在看,就是没有行动,好几个月了.日拱一卒 不期速成,先开始做吧,虽然感觉自己的代码博客真的不好,不过谁是完美的,先第一版,慢慢迭代完美了.
冥想功能,最近2个月喜欢冥想,喜欢用番茄钟工作,所以需要下载不少app,都是比较成熟的,但是总会有自己的想法,一个Android程序员那就自己实现自己的想法吧"因为有用所以有用"我最喜欢的话,所以多用了
目的:1.提高代码水平,时间充足没有公司上线压力也不需要过于关注ui(原谅我自己app的ui确实不好),可以好好写更加关注用户看不到的代码本身质量
2.研究明白知识点,不能只是会用了,不然面试真的说不出多深入的
3.使用新技术,在公司一般为了安全很少使用新技术,自己的当然怎么新怎么来,各种尝试了
4.满足自己的需求了,用自己的app感觉很爽.
一图胜千言,主要功能下两图


主要功能,
1.音乐播放器(MediaPlayer) 开始停止等
2.选择背景音乐类型(保存assets),播放时间(Select选择或者输入EditText输入)
3.自定义view,播放的进度,四部分组成中间时间,圆形,第一个圆弧,第二个圆弧(耗时最多,也是最有意思的,毕竟自定义控件才是难点了)
代码
1.音乐播放器MediaPlayer//播放器资源的准备工作 private void initMediaPlayer() { try { mAssets = this.getAssets(); //获取音乐文件 AssetFileDescriptor fileDescriptor = mAssets.openFd(musics[selectMusic]); //获取到播放类 mediaPlayer = new MediaPlayer(); //配置数据源 mediaPlayer.setDataSource(fileDescriptor.getFileDescriptor(), fileDescriptor.getStartOffset(), fileDescriptor.getLength()); //准备 mediaPlayer.prepare(); } catch (IOException e) { e.printStackTrace(); } //监听播放结束,循环播放,音乐文件可能过短需要循环 mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { @Override public void onCompletion(MediaPlayer mp) { if (!isPlayer) { mediaPlayer.start(); } else { } } } ); }
开始停止音乐播放case R.id.start: isPlayer = true; initMediaPlayer(); if (!mediaPlayer.isPlaying()) { String s = mTime.getText().toString(); playerAllTime = Integer.parseInt(s) * 60; //播放 mediaPlayer.start(); //开启计时器 handler.postDelayed(runnable, 1000); } break; case R.id.stop: isPlayer = false; if (mediaPlayer != null&&mediaPlayer.isPlaying()) { mediaPlayer.reset(); initMediaPlayer(); }
退出,关闭资源@Override public void onDestroy() { super.onDestroy(); if (mediaPlayer != null) { mediaPlayer.stop(); //释放播放器资源,一般再服务退出时执行 mediaPlayer.release(); } }
2.选择背景音乐类型(保存assets),播放时间(Select选择或者输入EditText输入)
直接2个自定义Dialog完成,RecyclerView要自己实现点击事件确实麻烦,不过效率更高
最上面CardView完全为了使用一下,以前都是自己绘制shape的直接用google提供的更简单一些
3.自定义view,核心部分,直接上代码,注释应该很完善了
`package w.com.wk.freelife.view;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;
//自定义圆弧
public class ArcView extends View {
private Paint mArcPaint, mCirclePaint, mTextPaint, mPaint;
private float length;
private float mRadius;
private float mCircleXY;
private float mSweepValue = 0;
private String mShowText = "0";
private RectF mRectF;
public ArcView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView();
}
public ArcView(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}
public ArcView(Context context) {
super(context);
initView();
}
private void initView() {
//圆形颜色
mCirclePaint = new Paint();
mCirclePaint.setColor(Color.parseColor("#E2E2E2"));
mCirclePaint.setAntiAlias(true);
//字体颜色
mTextPaint = new Paint();
mTextPaint.setTextSize(40);
mTextPaint.setAntiAlias(true);
mTextPaint.setColor(Color.parseColor("#2F2F2F"));
mTextPaint.setStrokeWidth(0);
//画弧线1
mPaint = new Paint();
mPaint.setStrokeWidth(20);
mPaint.setAntiAlias(true);
mPaint.setColor(Color.parseColor("#ffffff"));
mPaint.setStyle(Paint.Style.STROKE);
//画弧线2
mArcPaint = new Paint();
mArcPaint.setStrokeWidth(20);
mArcPaint.setAntiAlias(true);
mArcPaint.setColor(Color.parseColor("#00AAFE"));
mArcPaint.setStyle(Paint.Style.STROKE);
}
//android 系统回调onSizeChange获取宽度,确定半径等值
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
length = w;
mCircleXY = length / 2;
mRadius = (float) (length * 0.5 / 2);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 画圆
//x,y位置自定义view的中心点,mRadius半径
canvas.drawCircle(mCircleXY, mCircleXY, mRadius+160, mCirclePaint);
// // 画弧线oval :指定圆弧的外轮廓矩形区域。
// startAngle: 圆弧起始角度,单位为度。
// sweepAngle: 圆弧扫过的角度,顺时针方向,单位为度。
// useCenter: 如果为True时,在绘制圆弧时将圆心包括在内,通常用来绘制扇形。
// paint: 绘制圆弧的画板属性,如颜色,是否填充等。
//绘制圆弧的四个点
mRectF = new RectF((float) (length 0.2), (float) (length 0.2),
(float) (length 0.8), (float) (length 0.8));
canvas.drawArc(mRectF, 0, 360, false, mPaint);
canvas.drawArc(mRectF, 270, mSweepValue, false, mArcPaint);
// // 绘制文字
//测量字体宽度,我们需要根据字体的宽度设置在圆环中间
float textWidth = mTextPaint.measureText(mShowText);
canvas.drawText(mShowText, mCircleXY- textWidth / 2, mCircleXY, mTextPaint);
}
public void setProgress(float mSweepValue, int n) {
float a = mSweepValue;
if (a != 0) {
this.mSweepValue = (float) (360.0 * a);
mShowText = getTime(n);
} else {
this.mSweepValue = 0;
mShowText = 0 + "";
}
// postInvalidate invalidate都是实现界面刷新区别如下
// postInvalidate() 用在子线程向UI线程发送界面刷新消息请求,
// invalidate()调用直接在UI线程刷新界面, 子线程使用需要实例化一个Handler对象,并重写handleMessage方法调用invalidate()实现界面刷新;而在线程中通过sendMessage发送界面更新消息。
// 使用postInvalidate最简单,内部封装了和ui线程的操作
invalidate();
}
//秒转换成为分钟:秒
public String getTime(int time) {
if (time <= 0) {
return "0:00";
}
int minute = time / 60 % 60;
int second = time % 60;
return minute + ":" + second;
}
}`
Android RectF类的构造函数参数说明 ,一直使用但是这次深入研究才真正想明白,左上右下的意思,都是距离x,y轴的距离罢了
上图
public RectF (float left, float top, float right, float bottom)
比如new一个RecF类:
RectF rf1 = new RectF(100,100,200,200);
则在屏幕中的位置示意图为:

先这么多,不是很完美,选择时间音乐地方太丑了,这几天研究下最新的动画,换个动画玩玩.
项目地址