这个是实习的时候做的了,当时看了半本第一行代码就去上班,Java 都没学过,最后实习走的时候还做了挺多东西,这个音频播放器就是其一,现在写个博客纪念一下上班两年了,虽然还是菜狗程序员!

前言

实习的时候很菜,一直追求使用开源库,这个播放器也用到了几个有名的库,也不算强凑的吧。

  • Glide
  • ButterKnife
  • BaseRecyclerViewAdapterHelper

效果图我也不想放了,很 low,但能用,就自己记录下,如果希望借鉴的话继续看吧!

代码

  • AudioPlayerActivity
import android.animation.ObjectAnimator;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.animation.Animation;
import android.view.animation.LinearInterpolator;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;import com.bumptech.glide.Glide;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.bumptech.glide.load.resource.bitmap.CircleCrop;
import com.bumptech.glide.request.RequestOptions;import java.io.IOException;
import java.util.ArrayList;
import java.util.List;import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;/*** 低仿网易云播放器* @author :Silence* @date:2019/5/21 16:53*/
@SuppressWarnings("unused")
@SuppressLint("NonConstantResourceId")
public class AudioPlayerActivity extends AppCompatActivity {/*** 列表循环播放模式*/public static final int PLAY_MOD_LOOP = 0;/*** 单曲循环播放模式*/public static final int PLAY_MOD_SINGLE = 1;/*** 随机播放模式*/public static final int PLAY_MOD_RANDOM = 2;/*** 退出 activity*/@OnClick(R.id.play_audio_back)public void backClick(){ finish(); }/*** 标题*/@BindView(R.id.play_audio_title)TextView textTitle;/*** 中心图片 ImageView,可以获取到音乐的封面放这,靠Glide加载,及属性动画转起来*/@BindView(R.id.play_audio_center)ImageView imageCenter;/*** 上一首*/@BindView(R.id.last_audio)ImageView lastAudio;@OnClick(R.id.last_audio)public void last(){switch (playbackMode){case PLAY_MOD_LOOP:case PLAY_MOD_SINGLE:nowPosition--;if (nowPosition < 0) {//跳转循环nowPosition = mediaItems.size() - 1;}//播放startPlay();//选中歌曲变色audioAdapter.setSelect(nowPosition);audioAdapter.notifyDataSetChanged();break;case PLAY_MOD_RANDOM:nowPosition = (int) (Math.random()*mediaItems.size());startPlay();//选中歌曲变色audioAdapter.setSelect(nowPosition);audioAdapter.notifyDataSetChanged();break;default:}}/*** 播放或者暂停*/@BindView(R.id.startOrPause)ImageView startOrPause;@OnClick(R.id.startOrPause)public void pause(){if (mediaPlayer.isPlaying()) {//中间图片暂停旋转mAnimator.pause();mediaPlayer.pause();startOrPause.setImageResource(R.drawable.ic_pic_pause);} else {//中间图片继续旋转mAnimator.resume();mediaPlayer.start();startOrPause.setImageResource(R.drawable.ic_pic_play);}}/*** 下一首*/@BindView(R.id.next_audio)ImageView nextAudio;@OnClick(R.id.next_audio)public void next(){switch (playbackMode){case PLAY_MOD_LOOP:case PLAY_MOD_SINGLE:nowPosition++;//跳转循环if (nowPosition > (mediaItems.size() - 1)) {nowPosition = 0;}//播放startPlay();//选中歌曲变色audioAdapter.setSelect(nowPosition);audioAdapter.notifyDataSetChanged();break;case PLAY_MOD_RANDOM://随机nowPosition = (int) (Math.random()*mediaItems.size());startPlay();//选中歌曲变色audioAdapter.setSelect(nowPosition);audioAdapter.notifyDataSetChanged();break;default:}}/*** 进度条*/@BindView(R.id.seekBar)SeekBar seekBar;/*** 歌曲时间*/@BindView(R.id.audio_time)TextView textTime;/*** 播放模式,点击来回切换*/@BindView(R.id.playMode)ImageView playMode;@OnClick(R.id.playMode)public void playMode(){switch (playbackMode){case PLAY_MOD_LOOP:playbackMode = 1;playMode.setImageResource(R.drawable.pic_playmod_signle);break;case PLAY_MOD_SINGLE:playbackMode = 2;playMode.setImageResource(R.drawable.pic_playmod_random);break;case PLAY_MOD_RANDOM:playbackMode = 0;playMode.setImageResource(R.drawable.pic_playmod_loop);break;default:}}/*** 列表相关,仅文字列表,但和音频对应*/@BindView(R.id.audioList)RecyclerView audioList;AudioPlayListAdapter audioAdapter;List<String> data = new ArrayList<>();/*** 是否是网络资源*/boolean isNet = true;/*** 音频链接*/String link;/*** 列表中正在播放的位置*/private int nowPosition = 0;/*** 播放模式*/private int playbackMode = 0;/*** 音频列表*/List<Audio> mediaItems = new ArrayList<>();/*** 音频播放器*/MediaPlayer mediaPlayer;/*** 封面图片*/Bitmap albumBitmap;/*** 旋转动画*/ObjectAnimator mAnimator;/*** 处理 seekBar进度的重置和更新,要注意内存泄漏*/@SuppressLint("HandlerLeak")Handler handler = new Handler(){@SuppressLint("SetTextI18n")@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);if (msg.what == 0) {//得到当前进度int currentPosition = mediaPlayer.getCurrentPosition();int duration = mediaPlayer.getDuration();seekBar.setMax(duration);//设置seekBar的进度seekBar.setProgress(currentPosition);textTime.setText(formatSeconds(currentPosition / 1000)+ "/" + formatSeconds(duration / 1000));//定时更新,1秒removeMessages(0);sendEmptyMessageDelayed(0, 1000);}}};/*** 转换秒值为字符串* @param seconds 秒* @return 时分秒字符串*/private String formatSeconds(long seconds) {int temp;StringBuilder sb = new StringBuilder();if (seconds > 3600) {//时temp = (int) (seconds / 3600);sb.append((seconds / 3600) < 10 ? "0" + temp + ":" : temp + ":");}//分temp = (int) (seconds % 3600 / 60);sb.append((temp < 10) ? "0" + temp + ":" : "" + temp + ":");//秒temp = (int) (seconds % 3600 % 60);sb.append((temp < 10) ? "0" + temp : "" + temp);return sb.toString();}@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.component_audio);//全屏getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);ButterKnife.bind(this);init();//获取intent数据getIntentData();//启动播放startPlay();//设置歌曲列表适配器initAdapter();}private void init(){mediaPlayer = new MediaPlayer();//将 seekBar和播放器联系起来seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {@Overridepublic void onProgressChanged(SeekBar seekBar, int i, boolean b) {if (b) { mediaPlayer.seekTo(i); } }@Overridepublic void onStartTrackingTouch(SeekBar seekBar) { }@Overridepublic void onStopTrackingTouch(SeekBar seekBar) { }});}/*** 旋转图片*/private void rotateImage() {//旋转动画mAnimator = ObjectAnimator.ofFloat(imageCenter, "rotation", 0.0f, 360.0f);//设定转一圈的时间mAnimator.setDuration(20000);//设定无限循环mAnimator.setRepeatCount(Animation.INFINITE);// 循环模式mAnimator.setRepeatMode(ObjectAnimator.RESTART);// 匀速mAnimator.setInterpolator(new LinearInterpolator());//开始动画mAnimator.start();}private void getIntentData(){Intent intent = getIntent();isNet = intent.getBooleanExtra("isNet",false);if(isNet) {//网络链接link = intent.getStringExtra("link");}else {if(null != intent.getExtras()) {//noinspection uncheckedmediaItems = (List<Audio>) intent.getExtras().getSerializable("audioList");}//传入播放音乐在列表中位置nowPosition = intent.getIntExtra("position", 0);}}private void initAdapter() {audioList.setHasFixedSize(true);audioList.setLayoutManager(new LinearLayoutManager(this));//从音频列表中获取音频文件列表for (Audio audio : mediaItems){data.add(audio.getName());}//创建列表适配器audioAdapter = new AudioPlayListAdapter(R.layout.item_text_singleline,data);//选中项audioAdapter.setSelect(nowPosition);//列表为空时布局audioAdapter.setEmptyView(R.layout.view_empty_file, (ViewGroup) audioList.getParent());//单击列表项切换音频audioAdapter.setOnItemClickListener((adapter, view, position) -> {audioAdapter.setSelect(nowPosition);audioAdapter.notifyDataSetChanged();nowPosition = position;startPlay();});audioList.setAdapter(audioAdapter);}private void startPlay() {// 释放之前音频if (mediaPlayer != null) {mediaPlayer.reset();}try {assert mediaPlayer != null;mediaPlayer.setDataSource(AudioPlayerActivity.this, Uri.parse(link));mediaPlayer.prepareAsync();} catch (IOException e) {e.printStackTrace();}if (isNet){//解码mediaPlayer.setOnPreparedListener(mediaPlayer -> {mediaPlayer.start();//加载默认图片albumBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);Glide.with(AudioPlayerActivity.this).load(albumBitmap).apply(RequestOptions.bitmapTransform(new CircleCrop())).skipMemoryCache(true)                     // 不使用内存缓存.diskCacheStrategy(DiskCacheStrategy.NONE) // 不使用磁盘缓存.into(imageCenter);handler.sendEmptyMessage(0);});//播放完成监听mediaPlayer.setOnCompletionListener(mediaPlayer -> finish());mediaPlayer.setOnErrorListener((mediaPlayer, i, i1) -> {Toast.makeText(AudioPlayerActivity.this,"播放出错了",Toast.LENGTH_SHORT).show();finish();return true;});//网络资源关闭上下曲和播放模式lastAudio.setVisibility(View.GONE);nextAudio.setVisibility(View.GONE);playMode.setVisibility(View.GONE);}else{//设置标题为音频文件名textTitle.setText(mediaItems.get(nowPosition).getName());//解码mediaPlayer.setOnPreparedListener(mediaPlayer -> {mediaPlayer.start();//获取专辑封面图片albumBitmap = MediaUtiles.getArtwork(AudioPlayerActivity.this,mediaItems.get(nowPosition).getAudioId(),mediaItems.get(nowPosition).getAlbumId(),true,false);Glide.with(AudioPlayerActivity.this).load(albumBitmap).apply(RequestOptions.bitmapTransform(new CircleCrop())).skipMemoryCache(true)                     // 不使用内存缓存.diskCacheStrategy(DiskCacheStrategy.NONE) // 不使用磁盘缓存.into(imageCenter);handler.sendEmptyMessage(0);});//播放完成监听mediaPlayer.setOnCompletionListener(mediaPlayer -> next());mediaPlayer.setOnErrorListener((mediaPlayer, i, i1) -> {Toast.makeText(AudioPlayerActivity.this,"播放出错了",Toast.LENGTH_SHORT).show();next();return true;});//本地播放有循环模式mediaPlayer.setLooping((playbackMode == PLAY_MOD_LOOP));}//旋转中间图片rotateImage();}@Overrideprotected void onPause() {super.onPause();mediaPlayer.pause();}@Overrideprotected void onDestroy() {mediaPlayer.stop();mediaPlayer.release();handler.removeCallbacksAndMessages(null);super.onDestroy();}
}

这里就是一个 activity,记得在 manifest 里面注册。最烦那些放代码不给引入库的,这个全给了吧,还有几个用到的类,下面写。

  • XML 布局
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:background="@color/whitesmoke"android:orientation="vertical"><RelativeLayoutandroid:background="@color/gray"android:layout_width="match_parent"android:layout_height="40dp"><ImageViewandroid:id="@+id/play_audio_back"android:layout_alignParentStart="true"android:layout_marginStart="10dp"android:scaleType="centerCrop"android:src="@drawable/ic_pic_back"android:layout_width="wrap_content"android:layout_height="match_parent" /><TextViewandroid:id="@+id/play_audio_title"android:layout_width="wrap_content"android:layout_height="match_parent"android:layout_centerInParent="true"android:gravity="center"android:text="音频播放" /></RelativeLayout><ImageViewandroid:id="@+id/play_audio_center"android:layout_centerInParent="true"android:src="@drawable/pic_audio_default"android:layout_width="200dp"android:layout_height="200dp" /><LinearLayoutandroid:background="@color/colorGray"android:layout_alignParentBottom="true"android:layout_width="match_parent"android:gravity="center"android:layout_height="50dp"><LinearLayoutandroid:layout_width="wrap_content"android:layout_height="match_parent"><ImageViewandroid:id="@+id/last_audio"android:layout_width="40dp"android:layout_margin="5dp"android:padding="5dp"android:layout_height="match_parent"android:src="@drawable/ic_pic_previous"/><ImageViewandroid:id="@+id/startOrPause"android:layout_width="40dp"android:layout_margin="5dp"android:layout_height="match_parent"android:src="@drawable/ic_pic_pause"/><ImageViewandroid:id="@+id/next_audio"android:layout_width="40dp"android:padding="5dp"android:layout_margin="5dp"android:layout_height="match_parent"android:src="@drawable/ic_pic_next"/></LinearLayout><LinearLayoutandroid:layout_width="0dp"android:layout_weight="1"android:gravity="center"android:layout_marginEnd="3dp"android:layout_height="match_parent"><SeekBarandroid:id="@+id/seekBar"android:layout_width="0dp"android:layout_weight="1"android:layout_height="match_parent" /><TextViewandroid:id="@+id/audio_time"android:layout_width="wrap_content"android:layout_height="wrap_content"android:textColor="#ffffff"android:textSize="18sp"android:text="00:00/00:00"/></LinearLayout><ImageViewandroid:id="@+id/playMode"android:layout_width="30dp"android:layout_height="30dp"android:layout_marginStart="10dp"android:layout_marginEnd="10dp"android:src="@drawable/pic_playmod_loop"/></LinearLayout><android.support.v7.widget.RecyclerViewandroid:id="@+id/audioList"android:layout_alignParentEnd="true"android:layout_marginTop="40dp"android:layout_marginBottom="50dp"android:layout_width="200dp"android:layout_height="match_parent"/>
</RelativeLayout>

图标、颜色、toolbar如果要用的话,自己改改吧,原谅我之前的菜!

  • Audio
public class Audio implements Serializable {private String name;private Long duration;private Long size;private String address;private String artist;private Long audioId;private Long albumId;public Audio(String name, Long duration, Long size, String address, String artist) {this.name = name;this.duration = duration;this.size = size;this.address = address;this.artist = artist;this.audioId = -1L;this.albumId = -1L;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Long getDuration() {return duration;}public void setDuration(Long duration) {this.duration = duration;}public Long getSize() {return size;}public void setSize(Long size) {this.size = size;}public String getAddress() {return address;}public void setAddress(String address) {this.address = address;}public String getArtist() {return artist;}public void setArtist(String artist) {this.artist = artist;}public Long getAudioId() {return audioId;}public void setAudioId(Long audioId) {this.audioId = audioId;}public Long getAlbumId() {return albumId;}public void setAlbumId(Long albumId) {this.albumId = albumId;}
}

这个是本地音频的类,用的 Serializable,偷懒了,虽然我当时都不知道序列化有什么用,更不要说泛型了。

  • 适配器
import android.graphics.Color;
import android.support.annotation.Nullable;import com.chad.library.adapter.base.BaseQuickAdapter;
import com.chad.library.adapter.base.BaseViewHolder;import java.util.List;public class AudioPlayListAdapter extends BaseQuickAdapter<String, BaseViewHolder> {private int select = 0;public AudioPlayListAdapter(int layoutResId, @Nullable List<String> data) {super(layoutResId, data);}@Overrideprotected void convert(BaseViewHolder helper, String item) {helper.setText(R.id.item_text_singleline, item);//标记选中项if (helper.getLayoutPosition() == select){helper.setTextColor(R.id.item_text_singleline, Color.parseColor("#4caf50"));}else {helper.setTextColor(R.id.item_text_singleline, Color.parseColor("#8A8A8A"));}}public void setSelect(int select) {this.select = select;}
}

这里用到了 BARVH 里面的类,item_text_singleline 这个可以用 android 自带的单行布局,省的写了。

  • 获取音频封面工具类
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.ParcelFileDescriptor;import java.io.FileDescriptor;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;public class MediaUtiles {private static final Uri albumArtUri = Uri.parse("content://media/external/audio/albumart");/*** 获取默认专辑图片*/public static Bitmap getDefaultArtwork(Context context, boolean small) {BitmapFactory.Options opts = new BitmapFactory.Options();opts.inPreferredConfig = Bitmap.Config.RGB_565;if(small){  //返回小图片return BitmapFactory.decodeResource(context.getResources(), R.drawable.pic_audio_default);}return BitmapFactory.decodeResource(context.getResources(), R.drawable.pic_audio_default);}/*** 从文件当中获取专辑封面位图*/private static Bitmap getArtworkFromFile(Context context, long songid, long albumid){Bitmap bm = null;if(albumid < 0 && songid < 0) {throw new IllegalArgumentException("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 = null;try {pfd = context.getContentResolver().openFileDescriptor(uri, "r");} catch (FileNotFoundException e) {e.printStackTrace();}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;}/*** 获取专辑封面位图对象*/public static 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;}/*** 对图片进行合适的缩放*/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;}
}

这里实际上很多音乐都会把海报存到文件里面,获取到像 MP3 就可能拿到里面的封面。

结语

实际上这些代码写的不是很好,ButterKnife 现在用的也不多了,音频播放应该放到 service 里面去的,数据的序列化,布局的适配等都要提高。但是我也是想记录下自己当时有多菜,勉励吧!

end

低仿网易云音乐音频播放器!自动获取歌曲封面,并旋转相关推荐

  1. 仿网易云音乐的播放进度条

    仿网易云音乐的播放进度条,有三种状态:播放.暂停和拖动,只是实现了动画和主要的交互逻辑,其他细节(如暂停音乐的播放等)还需要自己完善: DKPlayerBar 是继承于UIControl的,如果想获取 ...

  2. python +tkinter 打造网易云音乐下载播放器

    先上图],本次爬取得是网易云音乐,因为接口很方便,还没试百度云音乐, 提供2种下载方式,单曲下载和url歌单链接批量下载,自动识别单曲或者是歌单链接,[正则表达式匹配] 由于搜索单曲,歌曲列表经过筛选 ...

  3. YesPlayMusic for Mac(高颜值网易云音乐第三方播放器)

    YesPlayMusic是一款在线第三方网易云播放器,软件的界面颜值更高更精简,对歌曲信息.歌词等做了极大的显示优化,同时也会有无损优质的音乐播放表现,软件支持mv播放,会显示歌曲的专辑信息,作为一款 ...

  4. 高颜值网易云音乐第三方播放器 YesPlayMusic Mac中文版 支持m1

    YesPlayMusic,一款高颜值的第三方网易云播放器,不仅设计精美,播放体验很优秀,而且可以听一些灰色和 VIP 的歌曲,支持网易云账号登录,还可以进行 MV 播放和歌词显示.支持 Unblock ...

  5. 热门免费的:YesPlayMusic Mac(高颜值网易云音乐第三方播放器)

    YesPlayMusic,一款高颜值的第三方网易云播放器,不仅设计精美,播放体验很优秀,而且可以听一些灰色和 VIP 的歌曲,支持网易云账号登录,还可以进行 MV 播放和歌词显示.支持 Unblock ...

  6. web音乐系统 javaweb音乐网站 低仿网易云音乐网站项目 期末课设 课设项目

    在一筹莫展的搞期末课设时,看到了这个up主的项目,简直是神仙下凡!!! 视频链接:https://www.bilibili.com/video/BV1i4411K7g4/?spm_id_from=33 ...

  7. 移动应用开发——uni-app框架 仿网易云音乐播放器学习心得

    目录 一.uni-app框架介绍 1.什么是 uni-app 2.为什么要选择uni-app 3.uni-app 统一规范 4.uni-app功能框架 二.开发工具与项目创建 1.开发工具 2.项目创 ...

  8. 仿网易云音乐html代码,仿网易云音乐外链播放器UI的HTML5音乐播放器插件

    简要教程 APlayer是一款仿网易云音乐外链播放器UI的HTML5音乐播放器插件.APlayer音乐播放器可以自定义歌曲封面,可以自定义同步歌词等,界面时尚大方,是一款非常好的HTML5音乐播放器插 ...

  9. html5自动播放音乐外链,仿网易云音乐外链播放器UI的HTML5音乐播放器插件

    APlayer是一款仿网易云音乐外链播放器UI的HTML5音乐播放器插件.APlayer音乐播放器可以自定义歌曲封面,可以自定义同步歌词等,界面时尚大方,是一款非常好的HTML5音乐播放器插件. 安装 ...

最新文章

  1. python 求list最小值的索引_【挑战自学Python编程】第六天:前五天都学了啥?
  2. VISIO2010界面介绍
  3. iOS开发UI篇—字典转模型
  4. Linux 文件系统与设备文件系统 (二)—— sysfs 文件系统与Linux设备模型
  5. 6个重要的.NET概念:栈,堆,值类型,引用类型,装箱,拆箱
  6. Typora+PicGo+github搭建免费稳定图床写博客如行云流水(mac+window)
  7. MongoDB中MapReduce介绍与使用
  8. mysql lock not wait_【MySQL】关于MySQL出现lock wait timeout exceeded 的解决方案
  9. 终面后拿offer几率_20届网易校招失败后的经验(现已拿offer)
  10. mysql2008安装失败_SQL Server 2008 安装失败问题总结
  11. html浮动垂直居中对齐,css如何设置垂直居中对齐?
  12. 8647服务器装系统,今天重新安装了系统,麻烦请红夜鬼先生进来帮我看一下
  13. Mac电脑怎样添加打印机?
  14. 数字电子计算机在线,(),第一台数字电子计算机问世。A、1944年B、1945年C、1946年 - 作业在线问答...
  15. 世界上最简单的会计书(资产负债表)
  16. flink 单作业模式部署提交作业爆:Trying to access closed classloader. Please check if you store classloaders direc
  17. SWFObject 2.0的使用说明
  18. updater-script
  19. vue 或 js 使用谷歌翻译实现国际化
  20. python 模拟ios提交网络_使用Xcode + Python进行IOS运动轨迹模拟

热门文章

  1. [Spring]如何使用bcrypt
  2. linux文件目录:Linux中各目录(文件夹)作用详解(持续更新)
  3. 提高接收器灵敏度为何需要光电二极管
  4. 0成本+0基础玩转跨境电商开店创业项目,月入数万元
  5. [转载]河南·荥阳第二届黄河樱花节 暨黄河樱花风
  6. JAVA电子病历系统源码,云端SaaS服务 前后端分离模式开发和部署
  7. 科技发展给生活带来的利与弊
  8. pdfium实现pdf转图片
  9. keil 软件如何生成.hex文件
  10. 将计算机与局域网互连学,计算机局域网组建与互连大学设计实施方案设计实施方案.doc...