Android 10.0 로 컬 음악 재생 실현(원본 다운로드 첨부)
이 글 은 안 드 로 이 드 소 백 이 작은 프로그램 을 쓰 고 있 습 니 다.내용 은 참고 만 할 수 있 습 니 다.부족 한 점 이 많 습 니 다.글 의 끝 에 전체 항목 의 다운로드 가 있 습 니 다.돈 이 필요 하지 않 습 니 다.문 제 를 해결 하 는 동시에 작은 칭찬 을 받 기 를 바 랍 니 다.이 항목 에는 아직도 부족 한 점 이 많다.예 를 들 어 버튼 에 그림 문 자 를 설정 하 는 것 은 Handler 에 게 맡 겨 야 한다.나 는 이 프로젝트 를 대충 완 성 했 을 뿐이다.테스트 환경:Android 10.0.실현:다음 곡 을 자동 으로 재생 하고 정상 적 인 음악의 기능,전체 화면 표시.
Android 10.0 은 안팎 으로 저장 되 어 있 습 니 다.응용 프로그램 은 메모 리 를 읽 을 수 있 는 권한 이 없습니다.설정 파일 에 application 에 속성 을 추가 해 야 합 니 다.android:request Legacy External Storage="true"입 니 다.노래 를 읽 을 수 는 없 지만 재생 할 수 없습니다.
2.효과 캡 처
캡 처 화면 이 다른 것 은 같은 시간 에 캡 처 한 것 이 아니 라 효과 그림 이기 때 문 입 니 다.
3.로 컬 음악 읽 기 및 노래 저장
① AndroidManifest 파일 에 권한 설정
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"></uses-permission>
② 현재 기본 적 인 핸드폰 은 정적 권한 을 사용 하 는 것 이 부족 하고 동적 권한 을 가 져 와 야 하기 때문에 MainActivity 에서 동적 으로 가 져 와 야 합 니 다.onCreate 방법 에서 방법 을 호출 해 야 합 니 다.
private void check(){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED ) {
requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
Log.d(TAG,"--------------------- -----------------");
}
if(checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE)!= PackageManager.PERMISSION_GRANTED ){
requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 2);
Log.d(TAG,"--------------------- -----------------");
}
}
}
③ 권한 을 되 돌 리 는 방법 은 Activity 의 onCreate 방법 과 같은 단계 이다.
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case 1:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Log.d(TAG, "--------------------- -----------------------------");
}
break;
case 2:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Log.d(TAG, "--------------------- -----------------------------");
}
break;
}
}
④ 도구 류 Mp3Info 를 만 들 고 음악 정 보 를 저장 하 는 데 사용 되 는 도구 류 는 주로 get 과 set 방법 이다.
public class Mp3Info {
private String url;//
private String title;//
private String artist;//
private long duration;//
private long id;
private long album;//
}
⑤ MusicUtil 류 를 만 들 고 ContentPorvider 의 인 터 페 이 스 를 통 해 노래 정 보 를 얻 습 니 다.
public class MusicUtil {
// UI
private static final String TAG="MusicUtil";
private static final Uri albumArtUri=Uri.parse("content://media/external/audio/albumart");
//
public static List<Mp3Info> getMp3InfoList(Context context){
Cursor cursor=context.getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null, null, null,null);
List<Mp3Info> mp3InfoList=new ArrayList<>();
while(cursor.moveToNext()){
Mp3Info mp3Info=new Mp3Info();
mp3Info.setUrl(cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.DATA)));//path
mp3Info.setTitle(cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.TITLE)));
mp3Info.setArtist(cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.ARTIST)));
mp3Info.setDuration(cursor.getLong(cursor.getColumnIndex(MediaStore.Audio.Media.DURATION)));
mp3Info.setId(cursor.getLong(cursor.getColumnIndex(MediaStore.Audio.Media._ID)));
mp3Info.setAlbum(cursor.getInt(cursor.getColumnIndex(MediaStore.Audio.Media.ALBUM_ID)));
mp3InfoList.add(mp3Info);
}
return mp3InfoList;
}
// , /
public static String formatTime(long time){
String min = time / (1000 * 60) + "";
String sec = time % (1000 * 60) + "";
if (min.length() < 2) {
min = "0" + time / (1000 * 60) + "";
} else {
min = time / (1000 * 60) + "";
}
if (sec.length() == 4) {
sec = "0" + (time % (1000 * 60)) + "";
} else if (sec.length() == 3) {
sec = "00" + (time % (1000 * 60)) + "";
} else if (sec.length() == 2) {
sec = "000" + (time % (1000 * 60)) + "";
} else if (sec.length() == 1) {
sec = "0000" + (time % (1000 * 60)) + "";
}
return min + ":" + sec.trim().substring(0, 2);
}
// , , ,qq ,
// uri 。
public Bitmap getArtwork(Context context, long song_id, long album_id, boolean allowdefalut, boolean small){
if(album_id < 0) {
if(song_id < 0) {
Bitmap bm = getArtworkFromFile(context, song_id, -1);
if(bm != null) {
return bm;
}
}
if(allowdefalut) {
return getDefaultArtwork(context, small);
}
return null;
}
ContentResolver res = context.getContentResolver();
Uri uri = ContentUris.withAppendedId(albumArtUri, album_id);
if(uri != null) {
InputStream in = null;
try {
in = res.openInputStream(uri);
BitmapFactory.Options options = new BitmapFactory.Options();
//
options.inSampleSize = 1;
//
options.inJustDecodeBounds = true;
// options
BitmapFactory.decodeStream(in, null, options);
/** N pixel 。 computeSampleSize **/
/** target 800 ,800 **/
if(small){
options.inSampleSize = computeSampleSize(options, 40);
} else{
options.inSampleSize = computeSampleSize(options, 600);
}
// , Bitmap
options.inJustDecodeBounds = false;
options.inDither = false;
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
in = res.openInputStream(uri);
return BitmapFactory.decodeStream(in, null, options);
} catch (FileNotFoundException e) {
Bitmap bm = getArtworkFromFile(context, song_id, album_id);
if(bm != null) {
if(bm.getConfig() == null) {
bm = bm.copy(Bitmap.Config.RGB_565, false);
if(bm == null && allowdefalut) {
return getDefaultArtwork(context, small);
}
}
} else if(allowdefalut) {
bm = getDefaultArtwork(context, small);
}
return bm;
} finally {
try {
if(in != null) {
in.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
return null;
}
/**
*
* @param context
* @param songid
* @param albumid
* @return
*/
private static Bitmap getArtworkFromFile(Context context, long songid, long albumid){
Bitmap bm = null;
if(albumid < 0 && songid < 0) {
throw new IllegalArgumentException("---------------------"+TAG+"Must specify an album or a song id");
}
try {
BitmapFactory.Options options = new BitmapFactory.Options();
FileDescriptor fd = null;
if(albumid < 0){
Uri uri = Uri.parse("content://media/external/audio/media/" + songid + "/albumart");
ParcelFileDescriptor pfd = context.getContentResolver().openFileDescriptor(uri, "r");
if(pfd != null) {
fd = pfd.getFileDescriptor();
}
} else {
Uri uri = ContentUris.withAppendedId(albumArtUri, albumid);
ParcelFileDescriptor pfd = context.getContentResolver().openFileDescriptor(uri, "r");
if(pfd != null) {
fd = pfd.getFileDescriptor();
}
}
options.inSampleSize = 1;
//
options.inJustDecodeBounds = true;
// options
BitmapFactory.decodeFileDescriptor(fd, null, options);
// 800pixel
// computeSampleSize
options.inSampleSize = 100;
// , Bitmap
options.inJustDecodeBounds = false;
options.inDither = false;
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
// options ,
bm = BitmapFactory.decodeFileDescriptor(fd, null, options);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
return bm;
}
/**
*
* @param context
* @return
*/
@SuppressLint("ResourceType")
public static Bitmap getDefaultArtwork(Context context, boolean small) {
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inPreferredConfig = Bitmap.Config.RGB_565;
if(small){ //
//return
BitmapFactory.decodeStream(context.getResources().openRawResource(R.drawable.default_picture), null, opts);
}
return BitmapFactory.decodeStream(context.getResources().openRawResource(R.drawable.default_picture), null, opts);
}
/**
*
* @param options
* @param target
* @return
*/
public static int computeSampleSize(BitmapFactory.Options options, int target) {
int w = options.outWidth;
int h = options.outHeight;
int candidateW = w / target;
int candidateH = h / target;
int candidate = Math.max(candidateW, candidateH);
if(candidate == 0) {
return 1;
}
if(candidate > 1) {
if((w > target) && (w / candidate) < target) {
candidate -= 1;
}
}
if(candidate > 1) {
if((h > target) && (h / candidate) < target) {
candidate -= 1;
}
}
return candidate;
}
}
⑥ 목록 에 adapter 를 설정 합 니 다.MyAdapter 클래스 를 새로 만 들 고 BaseAdapter 를 계승 한 다음 재 작성 한 getView 에 표 시 된 컨트롤 을 설정 합 니 다.
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if(convertView==null){
holder=new ViewHolder();
convertView=View.inflate(context, R.layout.list_item,null);
holder.tv_title=convertView.findViewById(R.id.tv_title);
holder.tv_artist=convertView.findViewById(R.id.tv_artist);
holder.tv_duration=convertView.findViewById(R.id.tv_duration);
holder.tv_position=convertView.findViewById(R.id.tv_position);
convertView.setTag(holder);
}else {
holder= (ViewHolder) convertView.getTag();
}
holder.tv_title.setText(list.get(position).getTitle());
holder.tv_artist.setText(list.get(position).getArtist());
long duration = list.get(position).getDuration();
String time= MusicUtil.formatTime(duration);
holder.tv_duration.setText(time);
holder.tv_position.setText(position+1+"");
if(currentItem == position){
holder.tv_title.setSelected(true);
holder.tv_position.setSelected(true);
holder.tv_duration.setSelected(true);
holder.tv_artist.setSelected(true);
}else{
holder.tv_title.setSelected(false);
holder.tv_position.setSelected(false);
holder.tv_duration.setSelected(false);
holder.tv_artist.setSelected(false);
}
return convertView;
}
class ViewHolder{
TextView tv_title;//
TextView tv_artist;//
TextView tv_duration;//
TextView tv_position;//
}
4.서 비 스 를 이용 하여 백 스테이지 재생bindService 를 사용 하면 Service 의 생명 주 기 는 Activity 의 생명 주기 와 연결 된다.MusicService 를 만 듭 니 다.주의:서 비 스 를 소각 할 때 는 음악 대상 을 release 해 야 합 니 다.
① Service 구현 기능,onBind 방법 에서 음악 재생 대상 을 예화
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG,"onBind is call");
myBinder=new MyBinder();
return myBinder;
}
② MyBinder()에 서 는 음악의 다양한 기능 을 구현 하 며,내부 클래스 를 사용 하고 있 으 며,초기 화 부분 은 소스 패키지 참조
public class MyBinder extends Binder{
private int index=0;//
//
public void playMusic(int index){
this.index=index;
try {
File file=new File(list.get(this.index).getUrl());
if(!file.exists()){
Log.d(TAG,"------------------------------ ------------------------------");
return ;
}else{
Log.d(TAG,"------------------------------ :"+file.getPath()+" ------------------------------");
}
if(mediaPlayer!=null){
mediaPlayer.reset();
mediaPlayer.release();
}
mediaPlayer=new MediaPlayer();
String str=list.get(this.index).getUrl();
mediaPlayer.setDataSource(str);
Log.d(TAG,list.get(this.index).getUrl()+"");
mediaPlayer.prepare();
mediaPlayer.start();
} catch (IOException e) {
e.printStackTrace();
}
}
//
public void pauseMusic(){
if(mediaPlayer.isPlaying()){
mediaPlayer.pause();
}
}
//
public void closeMusic(){
if(mediaPlayer!=null){
mediaPlayer.release();
}
}
//
public void nextMusic(){
if(index>=list.size()-1){
this.index=0;
}else{
this.index+=1;
}
playMusic(this.index);
}
//
public void preciousMusic(){
if(index<=0){
this.index=list.size()-1;
}else{
this.index-=1;
}
playMusic(this.index);
}
//
public int getProgress(int dex){
return (int)list.get(dex).getDuration();
}
public int getProgress(){
return (int)list.get(index).getDuration();
}
//
public int getPlayPosition(){
return mediaPlayer.getCurrentPosition();
}
//
public void seekToPosition(int m){
mediaPlayer.seekTo(m);
}
}
③ 메 인 액 티 비 티 에 귀속a.먼저 ServiceConnection 대상 을 예화 합 니 다.
private ServiceConnection connection=new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
myBinder= (MusicService.MyBinder) service;
seekBar.setMax(myBinder.getProgress());
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
//
if(fromUser){
myBinder.seekToPosition(seekBar.getProgress());
}
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
handler.post(runnable);
Log.d(TAG, "Service Activity ");
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
b.ui 구성 요소 의 변 화 를 제어 하기 위해 handler 가 필요 합 니 다.실례 화 는 onCreate 방법 에 넣 었 습 니 다.c.Runnable 대상 으로 seekbar 전진
private Runnable runnable=new Runnable() {
@Override
public void run() {
seekBar.setProgress(myBinder.getPlayPosition());
tv_leftTime.setText(time.format(myBinder.getPlayPosition())+"");
tv_rightTime.setText(time.format(myBinder.getProgress()-myBinder.getPlayPosition())+"");
if(myBinder.getProgress()-myBinder.getPlayPosition()<1000){//
runOnUiThread(new Runnable() {// ui ,
@Override
public void run() {
ib_next.performClick();
}
});
}
handler.postDelayed(runnable,1000);
}
};
d.onCreate 방법 에서 귀속
MediaServiceIntent =new Intent(this,MusicService.class);//MediaServiceIntent Intent
bindService(MediaServiceIntent,connection,BIND_AUTO_CREATE);
5.알림 표시 줄 알림 사용메모:알림 표시 줄 을 누 르 면 MainActivity 에서 MainActivity 로 이동 합 니 다.설정 파일 의 activity android:name=".MainActivity"가 필요 합 니 다.
android:launchMode="singleTask",단일 작업 으로 설정 합 니 다.
원본 패키지 에 배치 되 어 있 으 며,Api 26 레벨 이상 에 서 는 NotificationChannel 을 사용 해 야 합 니 다.
① 알림 이 촉발 하 는 PandingIntent 를 설정 하고 Action 인식 을 통 해 action 이 자신 에 게 정의 하 는 상수 이 며 setSound 는 소리 가 없습니다.RemoteViews 를 통 해 알림 표시 줄 구성 요소 의 단 추 를 실현 합 니 다.
//
private void setNotification(){
String channelID="cary";
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.O){
NotificationChannel channel=new NotificationChannel(channelID,"xxx",NotificationManager.IMPORTANCE_LOW);
manager.createNotificationChannel(channel);
}
Intent intent=new Intent(MainActivity.this,MainActivity.class);
PendingIntent pi=PendingIntent.getActivity(MainActivity.this,0,intent,0);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
notify=new Notification.Builder(MainActivity.this,channelID)
.setWhen(System.currentTimeMillis())
.setSound(null)
.build();
}
notify.icon=android.R.drawable.btn_star;
notify.contentIntent=pi;
notify.contentView=remoteViews;
notify.flags=Notification.FLAG_ONGOING_EVENT;
remoteViews.setOnClickPendingIntent(R.id.notice,pi);
//
Intent prevIntent=new Intent(BUTTON_PREV_ID);
PendingIntent prevPendingIntent=PendingIntent.getBroadcast(this,0,prevIntent,0);
remoteViews.setOnClickPendingIntent(R.id.widget_prev,prevPendingIntent);
//
Intent playIntent=new Intent(BUTTON_PLAY_ID);
PendingIntent playPendingIntent=PendingIntent.getBroadcast(this,0,playIntent,0);
remoteViews.setOnClickPendingIntent(R.id.widget_play,playPendingIntent);
//
Intent nextIntent=new Intent(BUTTON_NEXT_ID);
PendingIntent nextPendingIntent=PendingIntent.getBroadcast(this,0,nextIntent,0);
remoteViews.setOnClickPendingIntent(R.id.widget_next,nextPendingIntent);
//
Intent closeIntent=new Intent(BUTTON_CLOSE_ID);
PendingIntent closePendingIntent=PendingIntent.getBroadcast(this,0,closeIntent,0);
remoteViews.setOnClickPendingIntent(R.id.widget_close,closePendingIntent);
}
② 동적 등록 방송
//
private void initButtonReceiver(){
buttonBroadcastReceiver=new ButtonBroadcastReceiver();
IntentFilter intentFilter=new IntentFilter();
intentFilter.addAction(BUTTON_PREV_ID);
intentFilter.addAction(BUTTON_PLAY_ID);
intentFilter.addAction(BUTTON_NEXT_ID);
intentFilter.addAction(BUTTON_CLOSE_ID);
registerReceiver(buttonBroadcastReceiver,intentFilter);
}
③ 방송 을 표시 할 때 주의해 야 할 것 은 Activity 에서 이전 곡 이나 다음 수 도 를 클릭 할 때마다 이 방법 을 사용 하여 알림 표시 줄 의 제목 과 상태 앨범 을 새로 고 쳐 야 한 다 는 것 이다.
//
private void showNotification(){
if(isPlaying){
remoteViews.setImageViewResource(R.id.widget_play,R.drawable.stop);
}else{
remoteViews.setImageViewResource(R.id.widget_play,R.drawable.start);
}
remoteViews.setImageViewBitmap(R.id.widget_album,utils.getArtwork(MainActivity.this,list.get(music_index).getId(),list.get(music_index).getAlbum(),true,false));
remoteViews.setImageViewResource(R.id.widget_close,android.R.drawable.ic_menu_close_clear_cancel);
remoteViews.setTextViewText(R.id.widget_title,list.get(music_index).getTitle());
remoteViews.setTextViewText(R.id.widget_artist,list.get(music_index).getArtist());
remoteViews.setTextColor(R.id.widget_title,Color.BLACK);
remoteViews.setTextColor(R.id.widget_artist,Color.BLACK);
notify.contentView=remoteViews;
manager.notify(100,notify);
}
④ 알림 표시 줄 동작 수신,내부 클래스 사용
public class ButtonBroadcastReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
String action=intent.getAction();
Log.d(TAG,"-------------------- action:"+action+"--------------------------");
if(action.equals(BUTTON_PREV_ID)){
runOnUiThread(new Runnable() {
@Override
public void run() {
ib_precious.performClick();
return;
}
});
}
if(action.equals(BUTTON_PLAY_ID)){
runOnUiThread(new Runnable() {
@Override
public void run() {
ib_state.performClick();
return;
}
});
}
if(action.equals(BUTTON_NEXT_ID)){
runOnUiThread(new Runnable() {
@Override
public void run() {
ib_next.performClick();
return;
}
});
}
if(action.equals(BUTTON_CLOSE_ID)){
handler.removeCallbacks(runnable);
myBinder.closeMusic();
unbindService(connection);
if(remoteViews!=null){
manager.cancel(100);
}
unregisterReceiver(buttonBroadcastReceiver);
finish();
}
}
}
6.전체 화면 표시① AndroidManifest 파일 에 테마 스타일 android:theme="@style/theme.AppCompat.Light.NoAction Bar">
그리고 onCreate 방법 에서 set ContentView(R.layot.activitymain);이전
설정:
if(Build.VERSION.SDK_INT>=21){
View decorView=getWindow().getDecorView();
decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN|View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
getWindow().setStatusBarColor(Color.TRANSPARENT);
}
7.선 택 된 곡 의 스타일 설정① res 디 렉 터 리 에 있 는 drawable 자원 에서 selector 라 는 xml 파일 을 새로 만 들 고 속성 을 설정 합 니 다.
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:state_selected="false"
android:color="#FFFFFF"/>
<item
android:state_selected="true"
android:color="#FF7F00"/>
</selector>
② Adapter 에 getView 설정
currentItem == position){
holder.tv_title.setSelected(true);
holder.tv_position.setSelected(true);
holder.tv_duration.setSelected(true);
holder.tv_artist.setSelected(true);
}else{
holder.tv_title.setSelected(false);
holder.tv_position.setSelected(false);
holder.tv_duration.setSelected(false);
holder.tv_artist.setSelected(false);
}
메모:사용 할 때 수 동 으로 권한 을 설정 해 야 할 수도 있 습 니 다.코드 팩 안에 있 는 MusicPlayer\app\release 의 MusicPlayer.apk 는 app 설치 패키지 입 니 다.좋아요 와 댓 글 을 기대 합 니 다.
주소
안 드 로 이 드 10.0 로 컬 음악 재생 실현(소스 코드 다운로드)에 관 한 이 글 은 여기까지 소개 되 었 습 니 다.더 많은 안 드 로 이 드 10.0 로 컬 음악 재생 내용 은 우리 의 이전 글 을 검색 하거나 아래 의 관련 글 을 계속 찾 아 보 세 요.앞으로 많은 응원 바 랍 니 다!
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Bitrise에서 배포 어플리케이션 설정 테스트하기이 글은 Bitrise 광고 달력의 23일째 글입니다. 자체 또는 당사 등에서 Bitrise 구축 서비스를 사용합니다. 그나저나 며칠 전 Bitrise User Group Meetup #3에서 아래 슬라이드를 발표했...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.