android 音乐播放 圆形进度条
上次 上传gif后 没动 不知道这次会不会动 在依次吐槽 csdn的博客模板 原来要选择 无水印 我的天 gif才成功
上次写了一个音乐播放器 今天吧里面的一个效果写出来 写博客的习惯要慢慢养成 虽然平时上班忙 但是这不是借口
好了 正题开始
上面的效果 也不知道 能看到不 看不到 明天 再重新上传gif吧 家里电脑没有模拟器 做gif麻烦 回公司 在传
上面也有一些说明
圆形图片的显示 是网上的一个开源控件CircleImageView 先圆形图片的
在已有的轮子上面 稍加修改
形成上面的效果
主要是两点
进度条的绘制 来自歌曲的进度 按百分比来计算 当前播放的进度/歌曲总进度*360(一个圆)
主要是 进度条的绘制位置的确定
也就是弧形的绘制
弧形绘制 是这个RectF // 画圆弧的 画图分析 一半的一半才行
在这个CircleImageView 控件中 看懂核心就行
里面有这么一段
mBorderPaint.setStyle(Paint.Style.STROKE); //绘制最外面的圆弧
mBorderPaint.setAntiAlias(true);
mBorderPaint.setColor(mBorderColor);
mBorderPaint.setStrokeWidth(mBorderWidth);
mBitmapHeight = mBitmap.getHeight(); //显示图片的高
mBitmapWidth = mBitmap.getWidth(); //显示图片的宽
mBorderRect.set(0, 0, getWidth(), getHeight()); //一个矩形
mBorderRadius = Math.min((mBorderRect.height() - mBorderWidth) / 2, //绘制的圆的半径的确定
(mBorderRect.width() - mBorderWidth) / 2);
mDrawableRect.set(mBorderWidth, mBorderWidth, mBorderRect.width()
- mBorderWidth, mBorderRect.height() - mBorderWidth);
mDrawableRadius = Math.min(mDrawableRect.height() / 2,
mDrawableRect.width() / 2);
RectF.set(mBorderWidth/2, mBorderWidth/2, getWidth()-mBorderWidth/2,
getHeight()-mBorderWidth/2);
四个参数 确定这个矩形的 四个点
具体解释如下
修改的CircleImageView 文件
package com.daemon.musicseekbar;import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageView;public class CircleImageView extends ImageView {private static final ScaleType SCALE_TYPE = ScaleType.CENTER_CROP;private static final Bitmap.Config BITMAP_CONFIG = Bitmap.Config.ARGB_8888;private static final int COLORDRAWABLE_DIMENSION = 1;private static final int DEFAULT_BORDER_WIDTH = 0;private static final int DEFAULT_BORDER_COLOR = Color.BLACK;private final RectF mDrawableRect = new RectF();private final RectF mBorderRect = new RectF();private final Matrix mShaderMatrix = new Matrix();private final Paint mBitmapPaint = new Paint();private final Paint mBorderPaint = new Paint();private int mBorderColor = DEFAULT_BORDER_COLOR;private int mBorderWidth = DEFAULT_BORDER_WIDTH;private Bitmap mBitmap;private BitmapShader mBitmapShader;private int mBitmapWidth;private int mBitmapHeight;private float mDrawableRadius;private float mBorderRadius;private boolean mReady;private boolean mSetupPending;private Paint mBorderPaint1 = new Paint();;public float newAngle; // 画弧线的角度private RectF mBorderRect1 = new RectF();private MyApplication app;public CircleImageView(Context context) {super(context);}public CircleImageView(Context context, AttributeSet attrs) {this(context, attrs, 0);}public CircleImageView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);super.setScaleType(SCALE_TYPE);app = (MyApplication) ((MainActivity)(context)).getApplication();TypedArray a = context.obtainStyledAttributes(attrs,R.styleable.CircleImageView, defStyle, 0);mBorderWidth = a.getDimensionPixelSize(R.styleable.CircleImageView_border_width, DEFAULT_BORDER_WIDTH);mBorderColor = a.getColor(R.styleable.CircleImageView_border_color,DEFAULT_BORDER_COLOR);a.recycle();mReady = true;if (mSetupPending) {setup();mSetupPending = false;}}@Overridepublic ScaleType getScaleType() {return SCALE_TYPE;}@Overridepublic void setScaleType(ScaleType scaleType) {if (scaleType != SCALE_TYPE) {throw new IllegalArgumentException(String.format("ScaleType %s not supported.", scaleType));}}@Overrideprotected void onDraw(Canvas canvas) {if (getDrawable() == null) {return;}canvas.drawCircle(getWidth() / 2, getHeight() / 2, mDrawableRadius,mBitmapPaint); // 里面的图片canvas.drawCircle(getWidth() / 2, getHeight() / 2, mBorderRadius,mBorderPaint); // 边缘的线// 刷新 然后 在边缘 动态 画弧线if (mBorderWidth > 0) {canvas.drawArc(mBorderRect1, -90, app.newAngle, false, mBorderPaint1);}}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);setup();}public int getBorderColor() {return mBorderColor;}public void setBorderColor(int borderColor) {if (borderColor == mBorderColor) {return;}mBorderColor = borderColor;mBorderPaint.setColor(mBorderColor);invalidate();}public int getBorderWidth() {return mBorderWidth;}public void setBorderWidth(int borderWidth) {if (borderWidth == mBorderWidth) {return;}mBorderWidth = borderWidth;setup();}@Overridepublic void setImageBitmap(Bitmap bm) {super.setImageBitmap(bm);mBitmap = bm;setup();}@Overridepublic void setImageDrawable(Drawable drawable) {super.setImageDrawable(drawable);mBitmap = getBitmapFromDrawable(drawable);setup();}@Overridepublic void setImageResource(int resId) {super.setImageResource(resId);mBitmap = getBitmapFromDrawable(getDrawable());setup();}private Bitmap getBitmapFromDrawable(Drawable drawable) {if (drawable == null) {return null;}if (drawable instanceof BitmapDrawable) {return ((BitmapDrawable) drawable).getBitmap();}try {Bitmap bitmap;if (drawable instanceof ColorDrawable) {bitmap = Bitmap.createBitmap(COLORDRAWABLE_DIMENSION,COLORDRAWABLE_DIMENSION, BITMAP_CONFIG);} else {// 为0就自己加上需要的 要改不 就 传值 变化 或者 这里可以先测量一下?if (drawable.getIntrinsicWidth() <= 0) {int w = View.MeasureSpec.makeMeasureSpec(0,View.MeasureSpec.UNSPECIFIED); int h = View.MeasureSpec.makeMeasureSpec(0,View.MeasureSpec.UNSPECIFIED); measure(w, h); int height =getMeasuredHeight(); int width =getMeasuredWidth(); System.out.println(height +"---"+ width);bitmap = Bitmap.createBitmap(width,height,BITMAP_CONFIG);}else {bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(),drawable.getIntrinsicHeight(), BITMAP_CONFIG);}}Canvas canvas = new Canvas(bitmap);drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());drawable.draw(canvas);return bitmap;} catch (OutOfMemoryError e) {return null;}}private void setup() {if (!mReady) {mSetupPending = true;return;}if (mBitmap == null) {return;}mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP,Shader.TileMode.CLAMP);mBitmapPaint.setAntiAlias(true);mBitmapPaint.setShader(mBitmapShader);mBorderPaint.setStyle(Paint.Style.STROKE);mBorderPaint.setAntiAlias(true);mBorderPaint.setColor(mBorderColor);mBorderPaint.setStrokeWidth(mBorderWidth);// 画弧线的mBorderPaint1.setStyle(Paint.Style.STROKE);mBorderPaint1.setAntiAlias(true);mBorderPaint1.setColor(Color.parseColor("#F08080"));mBorderPaint1.setStrokeWidth(mBorderWidth);mBitmapHeight = mBitmap.getHeight();mBitmapWidth = mBitmap.getWidth();mBorderRect.set(0, 0, getWidth(), getHeight());mBorderRadius = Math.min((mBorderRect.height() - mBorderWidth) / 2,(mBorderRect.width() - mBorderWidth) / 2);mDrawableRect.set(mBorderWidth, mBorderWidth, mBorderRect.width()- mBorderWidth, mBorderRect.height() - mBorderWidth);mDrawableRadius = Math.min(mDrawableRect.height() / 2,mDrawableRect.width() / 2);// 画圆弧的 画图分析 一半的一半才行mBorderRect1.set(mBorderWidth/2, mBorderWidth/2, getWidth()-mBorderWidth/2, getHeight()-mBorderWidth/2);updateShaderMatrix();invalidate();}private void updateShaderMatrix() {float scale;float dx = 0;float dy = 0;mShaderMatrix.set(null);if (mBitmapWidth * mDrawableRect.height() > mDrawableRect.width()* mBitmapHeight) {scale = mDrawableRect.height() / (float) mBitmapHeight;dx = (mDrawableRect.width() - mBitmapWidth * scale) * 0.5f;} else {scale = mDrawableRect.width() / (float) mBitmapWidth;dy = (mDrawableRect.height() - mBitmapHeight * scale) * 0.5f;}mShaderMatrix.setScale(scale, scale);mShaderMatrix.postTranslate((int) (dx + 0.5f) + mBorderWidth,(int) (dy + 0.5f) + mBorderWidth);mBitmapShader.setLocalMatrix(mShaderMatrix);}}
XML布局
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context="com.daemon.musicseekbar.MainActivity" xmlns:app="http://schemas.android.com/apk/res/com.daemon.musicseekbar"><TextView android:layout_width="match_parent"android:layout_height="wrap_content"android:text="取出本地歌曲的前10首 随机播放一首 \n歌曲封面图片为 显示图片 \n圆形进度条随着歌曲播放而改变 \nCircleImageView 这个控件是github上面的 \n其实自定义一个也可以 但是这里有现成的轮子 就用了\n但是也 稍加修改了 因为外面的圆心进度滚动 也要计算"/><com.daemon.musicseekbar.CircleImageViewandroid:layout_centerInParent="true"android:id="@+id/civ_show"android:layout_width="200dp"android:layout_height="200dp"app:border_width="5dp"app:border_color="#ffffff"android:src="@drawable/ic_launcher"/><LinearLayout android:layout_alignParentBottom="true"android:layout_width="match_parent"android:layout_height="wrap_content"><TextView android:id="@+id/tv_music_name"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="歌曲名"/><Button android:id="@+id/bt_play"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="播放"/><Button android:id="@+id/bt_change"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="换一首"/></LinearLayout>
</RelativeLayout>
MainActivity 显示图片还是用了Imageloader 因为要显示 歌曲的专辑图片 眨眼方便很多 大晚上的也要睡觉了
package com.daemon.musicseekbar;import java.io.IOException;
import java.util.ArrayList;
import java.util.Random;import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.assist.ImageScaleType;import android.support.v7.app.ActionBarActivity;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.provider.MediaStore;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;public class MainActivity extends ActionBarActivity {protected static final int UPDATE_MUISC_COUNT = 0;private CircleImageView civ_show;private Cursor c;String path;private com.daemon.musicseekbar.MusicData musicDate;Handler handler = new Handler() {private int play_time;public void handleMessage(Message msg) {switch (msg.what) {case UPDATE_MUISC_COUNT:// 随机 选取一首int random = new Random().nextInt(musicDate._paths.size());path = musicDate._paths.get(random);tv_music_name.setText(musicDate._titles.get(random));String uri = "content://media/external/audio/albumart/"+ musicDate._album_ids.get(random);imageLoader.displayImage(uri, civ_show, options,new ImageLoaderSetting.AnimateFirstDisplayListener());break;case 1:// 用一个计时器 来 遍历 存在 app 中的数字play_time = mp.getCurrentPosition();app.newAngle = ((float) mp.getCurrentPosition()/ (float) mp.getDuration() * 360);// System.out.println("播放 进度 移动 角度" + app.newAngle);civ_show.invalidate();break;default:break;}};};Runnable runable = new Runnable() {@Overridepublic void run() {while (true) {SystemClock.sleep(1000);if (mp != null && mp.isPlaying()) {Message msg = Message.obtain();msg.what = 1;handler.sendMessage(msg);}}// handler.postDelayed(this, 1000); // 1s更新一次}};private TextView tv_music_name;private Button bt_play;private ImageLoader imageLoader;private DisplayImageOptions options;private Button bt_change;private MediaPlayer mp;private MyApplication app;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);app = (MyApplication) getApplication();imageLoader = ImageLoader.getInstance();configOptions();civ_show = (CircleImageView) findViewById(R.id.civ_show);tv_music_name = (TextView) findViewById(R.id.tv_music_name);bt_play = (Button) findViewById(R.id.bt_play);bt_change = (Button) findViewById(R.id.bt_change);mp = new MediaPlayer();bt_change.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {// TODO Auto-generated method stub// 随机 选取一首int random = new Random().nextInt(musicDate._paths.size());path = musicDate._paths.get(random);tv_music_name.setText(musicDate._titles.get(random));String uri = "content://media/external/audio/albumart/"+ musicDate._album_ids.get(random);imageLoader.displayImage(uri, civ_show, options,new ImageLoaderSetting.AnimateFirstDisplayListener());mp.reset();try {mp.setDataSource(path);mp.prepare();mp.start();mp.seekTo(30 * 1000);} catch (IllegalArgumentException | SecurityException| IllegalStateException | IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}});bt_play.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {Toast.makeText(MainActivity.this, path, 0).show();try {if (mp.isPlaying()) {mp.pause();} else {mp.reset();mp.setDataSource(path);mp.prepare();mp.start();mp.seekTo(30 * 1000);}} catch (IllegalArgumentException | SecurityException| IllegalStateException | IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}});// 随便播放本地一首音乐getLocalMusic();new Thread(runable).start();}private void configOptions() {options = new DisplayImageOptions.Builder().showImageOnLoading(R.drawable.ic_launcher).showImageForEmptyUri(R.drawable.ic_launcher).imageScaleType(ImageScaleType.IN_SAMPLE_INT)// 图片缩放方式.showImageOnFail(R.drawable.ic_launcher).cacheInMemory(true).cacheOnDisc(true).bitmapConfig(Bitmap.Config.RGB_565)// .displayer(new RoundedBitmapDisplayer(90)).build();}private void getLocalMusic() {c = getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,new String[] { MediaStore.Video.Media.TITLE, // 音乐名MediaStore.Audio.Media.DURATION, // 音乐的总时间MediaStore.Audio.Media.ARTIST, // 艺术家MediaStore.Audio.Media._ID, // id号MediaStore.Audio.Media.DISPLAY_NAME, // 音乐文件名MediaStore.Audio.Media.DATA, // 音乐文件的路径MediaStore.Audio.Media.ALBUM_ID // 封面要用的}, null,// 查询条件,相当于sql中的where语句null, // 查询条件中使用到的数据null);// 查询结果的排序方式musicDate = new MusicData();musicDate._ids = new ArrayList<Integer>();musicDate._titles = new ArrayList<String>();musicDate._paths = new ArrayList<String>();musicDate._singers = new ArrayList<String>();musicDate._album_ids = new ArrayList<Integer>();musicDate._durations = new ArrayList<Long>();// 异步加载new Thread(new Runnable() {@Overridepublic void run() {c.moveToFirst(); // 第一个for (int i = 0; i <= 10; i++) {// 大于30s才可以进来System.out.println("歌曲时间 " + c.getLong(1));if (c.getLong(1) > 30000) {musicDate._durations.add(c.getLong(1));musicDate._ids.add(c.getInt(3));musicDate._titles.add(c.getString(0));musicDate._paths.add(c.getString(5)); //musicDate._singers.add(c.getString(2));musicDate._album_ids.add(c.getInt(6));// 扫描一首 进度条 更新 上面的本地歌曲 更新 下面的 扫描地址音乐更}c.moveToNext();}// 通知 之前 将数据 保留下Message msg = Message.obtain();msg.what = UPDATE_MUISC_COUNT;handler.sendMessage(msg);}}).start();}@Overridepublic boolean onCreateOptionsMenu(Menu menu) {// Inflate the menu; this adds items to the action bar if it is present.getMenuInflater().inflate(R.menu.main, menu);return true;}@Overridepublic boolean onOptionsItemSelected(MenuItem item) {// Handle action bar item clicks here. The action bar will// automatically handle clicks on the Home/Up button, so long// as you specify a parent activity in AndroidManifest.xml.int id = item.getItemId();if (id == R.id.action_settings) {return true;}return super.onOptionsItemSelected(item);}
}
MyApplication
package com.daemon.musicseekbar;import java.io.File;import com.nostra13.universalimageloader.cache.disc.impl.UnlimitedDiscCache;
import com.nostra13.universalimageloader.cache.disc.naming.Md5FileNameGenerator;
import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.ImageLoaderConfiguration;
import com.nostra13.universalimageloader.core.assist.QueueProcessingType;
import com.nostra13.universalimageloader.utils.StorageUtils;import android.app.Application;public class MyApplication extends Application {public float newAngle=0; //开始角度@Overridepublic void onCreate() {// TODO Auto-generated method stubsuper.onCreate();//设置缓存 路径File cacheDir = StorageUtils.getOwnCacheDirectory(getApplicationContext(), "imageloader_Dting/Cache"); /*** imageload 基本配置 初始化ImageLoader*/ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(getApplicationContext()) .memoryCacheExtraOptions(100, 100).threadPoolSize(3).threadPriority(Thread.NORM_PRIORITY - 2) .denyCacheImageMultipleSizesInMemory() .discCacheFileNameGenerator(new Md5FileNameGenerator()) .tasksProcessingOrder(QueueProcessingType.LIFO).discCache(new UnlimitedDiscCache(cacheDir)) //设置缓存路径.writeDebugLogs()//.enableLogging() // Not necessary in common .build(); ImageLoader.getInstance().init(config); }
}
Imageloader用法 应该都会吧 相关的就不说了
MusicData 根据代码 可以自己创建实体 封装就是
基本差不多就这样了 明天看图传成功了没
android 音乐播放 圆形进度条相关推荐
- Vue2.0+SVG实现音乐播放圆形进度条组件,传入实时百分比实现圆圈进度动画效果
vue2.0+SVG实现音乐播放圆形进度条组件,传入实时百分比实现圆圈进度动画效果 需求分析: 类似于大多数音乐播放器中等mini播放器控制按钮,显示播放进度,实时更新进度. progress-cir ...
- android音乐播放器进度条误差研究
2019独角兽企业重金招聘Python工程师标准>>> 如何做一个"流畅"而且"准确"的进度条? 流畅!由两个条件决定,更新的频率 和 更新的 ...
- android音乐播放器进度条研究
如何做一个"流畅"而且"准确"的进度条? 流畅!由两个条件决定,更新的频率 和 更新的精度 频度和精度要相适应,才能保证流畅,并不是精度越高越好,也不是频度越高 ...
- Android自定义控件NumberCircleProgressBar(圆形进度条)的实现
Android自定义控件NumberCircleProgressBar(圆形进度条)的实现
- android 自定义音乐圆形进度条,Android自定义View实现音频播放圆形进度条
本篇文章介绍自定义View配合属性动画来实现如下的效果 实现思路如下: 根据播放按钮的图片大小计算出圆形进度条的大小 根据音频的时间长度计算出圆形进度条绘制的弧度 通过Handler刷新界面来更新圆形 ...
- android实现音乐播放器(进度条)
|--效果图 |--依赖3个对象 MediaPlayer:实现音乐播放,暂停,缓冲. SeekBar:滑动的进度条. java.util.Timer:定时器,时时更新进度条. |--main. ...
- MediaPlayer 音乐播放器进度条
今天学渣研究了一下使用MediaPlayer播放音乐时添加进度条,进度条现在用的是android自带的seekbar,后期会跟换UI的,在之前能够播放音乐的基础上,现在添加的主要功能有两个: 1实时显 ...
- android欢迎页圆形倒计时,android 欢迎页圆形进度条倒计时功能
常见app欢迎页圆形进度条倒计时功能,可设置显示文字,进度条颜色,宽度,倒计时时间,内圆颜色.设置进度条类型 顺数进度条(0-100)还是倒数进度条(100-0): 先上效果图: 下面介绍实现逻辑: ...
- Android可触摸圆形进度条,Android 可滚动圆形进度条 滑块和进度在进度条上面跟着滚动...
Android 可滚动圆形进度条 滑块和进度在进度条上面跟着滚动.package com.example.test; import android.content.Context; import an ...
最新文章
- 汉语转拼音工具、新华字典API——两个支持Python的中文资源
- SpringBoot第十四篇:在springboot中用redis实现消息队列
- Vue根据条件添加click事件
- 很好的阻止了事件的发生_请定好您的闹钟,八月,夜空中将发生这13件超酷的天文事件...
- Android UI学习 - GridView和ImageView的使用
- 手写实现RPC框架基础功能
- linux内核设计与实现 中文第三版 pdf_大牛推荐的5本 Linux 经典必读书
- vue watch 第一次不执行_Vue 实现前进刷新,后退不刷新的效果
- 前端学习(1305):项目依赖
- (转)OAuth 2.0的设计思路
- 在centos 6.5下安装svn (Subversion)
- bShare一个强大的网页分享插件
- 计算机网络原理填空题
- linux环境 下载Neo4j
- 40163 php,【PHP】微信支付JsApi 40163错误,_PHP_ 少侠科技
- 浅谈一下量化交易与程序化交易
- 实现内网穿透,个人电脑秒变服务器
- mongodb 服务器性能监控,MongoDB监控
- 青春期玩游戏不想上学怎么办?
- 粤嵌6818开发板项目