应项目需要我这边要定义一个播放录音的控件,效果如图:
这边先讲一下具体的初版思路吧
1、首先我的思路是复杂的问题简单化,先把这个控件拆分为:上面的刻度和下面的进度条
2、刻度可以根据录音的具体时间来画,下面的进度可以继承SeekBar来实现
3、然后将两个控件的组合在一起放在一个自定义的Relativelayout中,组合成一个控件
先上一下效果动图
下面是上代码时间:
一:首先来画刻度尺的部分
刻度尺的部分是比较简单的这里我自定义了一个控件继承View然后同onDraw来画出这个刻度尺来
如果要画出刻度尺则必须知道自定义控件在屏幕的中位置,下面是获取控件在屏幕中具体位置的方法
下面是获取屏幕宽度的方法
WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); DisplayMetrics dm = new DisplayMetrics(); windowManager.getDefaultDisplay().getMetrics(dm); screenWidth = dm.widthPixels;
下面是获取视图绝对坐标和相对坐标的方法
getLocationOnScreen ,计算该视图在全局坐标系中的x,y值,(注意这个值是要从屏幕顶端算起,也就是索包括了通知栏的高度)//获取在当前屏幕内的绝对坐标 getLocationInWindow ,计算该视图在它所在的widnow的坐标x,y值,//获取在整个窗口内的绝对坐标 (不是很理解= =、) getLeft ,getTop,getBottom,getRight,这一组是获取相对在它父亲里的坐标 如果在Activity的OnCreate()事件输出那些参数,是全为0,要等UI控件都加载完了才能获取到这些。
可在onMeasure中中设置自定义控件的高度
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int widthMode = MeasureSpec.getMode(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); int width; int height; if (heightMode == MeasureSpec.EXACTLY) { height = heightSize; } else { height = Math.min(mMinHeight, heightSize); } width = widthSize; setMeasuredDimension(width, height); }
控件的宽和高确定好之后接下来就是要画刻度值了,刻度值分为两部分,一部分是水平的横线另一部分是
竖直的竖杠,横线比较简单至于竖线考虑到录音的时间是从一到无穷大的,于是这竖线的部分就需要分一分了
首先是1-6s的时候分别画录取时间加一的竖线,大于6的时候则要将对录音的时间进行三等分或者两等分,把时间分的均匀一点就行了,上面的刻度只是个大概的显示所以尽力做到平均就行了。
线画完之后就要画时间的显示了时间的显示如下
将时间进行等分的处理函数
private int courseTime(int record, int time) { if (record % 6 == 0) {//整除6则返回6的整倍数 return record / 6 * time; } else if (record % 3 == 0) {//整除3 if (time % 2 == 0) {//三等分的位置 return (record / 3) * (time / 2); } else {//其他的位置 return ((record / 3) * ((time + 1) / 2) - ((record / 3) * ((time - 1) / 2))) / 2 + ((record / 3) * ((time - 1) / 2)); } } else if (record % 2 == 0) {//能整除2的数值 if (time % 3 == 0) {//进行二等分的位置 return (record / 2) * (time / 3); } else {//其他位置 if ((record / 2) % 3 == 1) {//二等分之后再三等分进行余数的分配 if (time < 3) {//前半部分的二等分 return (record / 2 / 3 * time); } else {//后半部分的二等分 return (record / 2 + record / 2 / 3 * (time - 3)); } } else {//同上 if ((record / 2) % 3 == 2) { if (time < 3) { return (record / 2 / 3 * time + time); } else { return (record / 2 + record / 2 / 3 * (time - 3) + (time - 3)); } } } } } else {//质数的分配 if (time == 0) { return 0; } else if (time == 3) { return record / 2; } else if (time == 6) { return record; } else if (time < 3) { if ((record / 2) % 3 == 1) { return (record / 2 / 3 * time); } else if ((record / 2) % 3 == 2) { return (record / 2 / 3 * time + time); } else { return record / 2 / 3 * time; } } else { if ((record / 2 + 1) % 3 == 1) { return (record / 2 + record / 2 / 3 * (time - 3)); } else if ((record / 2) % 3 == 2) { return (record / 2 + record / 2 / 3 * (time - 3) + (time - 3)); } else { return record / 2 + (record / 2 + 1) / 3 * (time - 3); } } } return 0; }
直接获取绘制字符串的宽度
textPaint.measureText(str)
将int值的秒级数转换为时分秒格式
private String getCourseTime(Integer seconds) { int day = seconds / (60 * 60 * 24);//换成天 int hour = (seconds - (60 * 60 * 24 * day)) / 3600;//总秒数-换算成天的秒数=剩余的秒数 剩余的秒数换算为小时 int minute = (seconds - 60 * 60 * 24 * day - 3600 * hour) / 60;//总秒数-换算成天的秒数-换算成小时的秒数=剩余的秒数 剩余的秒数换算为分 int second = seconds - 60 * 60 * 24 * day - 3600 * hour - 60 * minute;//总秒数-换算成天的秒数-换算成小时的秒数-换算为分的秒数=剩余的秒数 if (hour != 0) { return (hour >= 10 ? hour : "0" + hour) + ":" + (minute >= 10 ? minute : "0" + minute) + ":" + (second >= 10 ? second : "0" + second); } else if (minute != 0) { return (minute >= 10 ? minute : "0" + minute) + ":" + (second >= 10 ? second : "0" + second); } else { return "00:" + (second >= 10 ? second : "0" + second); } }
然后直接在几道竖线下画平分的时间就行了,至此刻度尺就制作完毕了。
二:接下来就是下面的进度条部分了,这个部分我是继承了SeekBar的自定义View制作的
我们也分为三步
1、首先是红色的游标
把默认的游标去除
android:duplicateParentState="true" android:thumb="@null"要画出完整的图标就必须把进度条的背景放大
//设置控件的padding 给提示指示图标留出位置 setPadding(linePadding, (int) Math.ceil(imageHeight) - 30, linePadding, 25);
2、就是进度条的颜色、进度和上面的刻度尺要保持两端相同,我的思路是:①进度条的长度保持和上面的刻度尺长度相同 ②画一个图片来覆盖进度条默认的进度图片 ③画一个半透明的灰色横线来作为进度
rectSeek = this.getProgressDrawable().getBounds(); float bm_x = rectSeek.width() * getProgress() / getMax() + (linePadding - (int) Math.ceil(imageWidth) / 2);
①
//设置控件的padding 给提示指示图标留出位置 setPadding(linePadding, (int) Math.ceil(imageHeight) - 30, linePadding, 25);
②
图片使用一个xml格式的shape做的可以添加起始、中部和结尾的颜色
canvas.drawBitmap(seekbarThumbBitmap, 0, (int) Math.ceil(imageHeight) - 28, cursorImagePaint);//绘制假的进度条背景色
③
canvas.drawLine(0, (int) Math.ceil(imageHeight) - 28, bm_x + (int) Math.ceil(imageWidth) / 2, (int) Math.ceil(imageHeight) - 28, seekbarThumbPaint);
3、下面一个就是画tag的功能了,这个主要就是画一个红色的矩形
根据打印tag的时间来确定红色矩形的位置就可以了
mBarRect.left = getPaddingLeft(); mBarRect.right = getWidth() - getPaddingRight(); //final boolean isLayoutRtl = getLayoutDirection() == View.LAYOUT_DIRECTION_RTL; float cx; //float cy = getHeight() / 2 - getPaddingBottom() / 2 - 2; float barRectwidth = mBarRect.width() - 50;
cx = mBarRect.left + barRectwidth * (mMarks.get(i).postion / (float) mDuration); //} //radius = getResources().getDimension(R.dimen.panit_line_space) / 3; //canvas.drawCircle(cx, cy, radius, mShapePaint); //canvas.drawBitmap(mBitmap, cx, cy, mPaintTag); canvas.drawRect(cx, (int) Math.ceil(imageHeight) - 28, cx + tagWidth, (int) Math.ceil(imageHeight) - 28 + tagHeight, tagPaint);至此这个控件就基本结束了,下面就是要合入record的代码中了。