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;
		}
	}

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


<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
		圆形进度条随着歌曲播放而改变 \n
		CircleImageView 这个控件是github上面的 \n
		其实自定义一个也可以 但是这里有现成的轮子 就用了\n
		但是也 稍加修改了 因为外面的圆心进度滚动 也要计算
		"
        />
    <com.daemon.musicseekbar.CircleImageView
        android: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() {
		@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<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() {
			@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  根据代码 可以自己创建实体 封装就是


基本差不多就这样了 明天看图传成功了没   



  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值