android 音乐播放 圆形进度条

上次 上传gif后 没动 不知道这次会不会动 在依次吐槽 csdn的博客模板   原来要选择 无水印 我的天 gif才成功


android 音乐播放 圆形进度条_第1张图片


上次写了一个音乐播放器 今天吧里面的一个效果写出来 写博客的习惯要慢慢养成 虽然平时上班忙 但是这不是借口


好了 正题开始 

上面的效果 也不知道 能看到不 看不到 明天 再重新上传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);

四个参数 确定这个矩形的 四个点

具体解释如下

android 音乐播放 圆形进度条_第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;
		}
	}

	@Override
	public ScaleType getScaleType() {
		return SCALE_TYPE;
	}

	@Override
	public void setScaleType(ScaleType scaleType) {
		if (scaleType != SCALE_TYPE) {
			throw new IllegalArgumentException(String.format(
					"ScaleType %s not supported.", scaleType));
		}
	}

	@Override
	protected 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);
		}

	}

	@Override
	protected 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();
	}

	@Override
	public void setImageBitmap(Bitmap bm) {
		super.setImageBitmap(bm);
		mBitmap = bm;
		setup();
	}

	@Override
	public void setImageDrawable(Drawable drawable) {
		super.setImageDrawable(drawable);
		mBitmap = getBitmapFromDrawable(drawable);
		setup();
	}

	@Override
	public 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布局




    
    

    
        
        

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() {
		@Override
		public 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;

	@Override
	protected 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() {

			@Override
			public 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 block
					e.printStackTrace();
				}

			}
		});

		bt_play.setOnClickListener(new OnClickListener() {

			@Override
			public 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 block
					e.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();
		musicDate._titles = new ArrayList();
		musicDate._paths = new ArrayList();
		musicDate._singers = new ArrayList();
		musicDate._album_ids = new ArrayList();
		musicDate._durations = new ArrayList();

		// 异步加载
		new Thread(new Runnable() {
			@Override
			public 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();

	}

	@Override
	public 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;
	}

	@Override
	public 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;  //开始角度

	
	@Override
	public void onCreate() {
		// TODO Auto-generated method stub
		super.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)