Android?Studio實(shí)現(xiàn)音樂播放器2.0的全過程
一、引言
我在一年前發(fā)過一篇用Android實(shí)現(xiàn)音樂播放器的教程:Android Studio如何實(shí)現(xiàn)音樂播放器(簡(jiǎn)單易上手)。當(dāng)時(shí)實(shí)現(xiàn)的功能也很簡(jiǎn)單,就是播放音樂、暫停音樂、繼續(xù)播放、退出播放、顯示音樂列表和專輯封面的功能。如下圖所示:
期間有很多同學(xué)問到我是否可以加上一首下一首功能,確實(shí)可以加,只要獲取到歌曲文件的下標(biāo)position即可實(shí)現(xiàn)此功能,不難。但是因?yàn)閷懲赀@個(gè)最初版本后就沒有當(dāng)時(shí)那種心境再改了,我想大家都會(huì)有這種體會(huì)。
雖然前段時(shí)間很忙,還是沉下心來回顧了自己的音樂播放器項(xiàng)目,也發(fā)現(xiàn)了很多不足的地方,然后進(jìn)行了優(yōu)化升級(jí),主要有三點(diǎn):
增加了上一首下一首功能更換了按鈕樣式,使用更個(gè)性化的按鈕增加了大多數(shù)代碼的注釋,做到臨缺勿濫
所以,這篇博客就是對(duì)最初版本(1.0版本)的一個(gè)完善,即2.0版本,話不多說,下面開始。
二、項(xiàng)目概述
1、需求分析
綜合運(yùn)用UI界面設(shè)計(jì)、數(shù)據(jù)存儲(chǔ)、Activity(活動(dòng))、Service(服務(wù))、MusicPlayer(音樂播放類)、ListView(列表)等知識(shí),設(shè)計(jì)開發(fā)一款具有音樂列表的音樂播放器。
2、設(shè)計(jì)分析
整個(gè)項(xiàng)目包含五個(gè)java文件和五個(gè)layout文件,因?yàn)槭潜容^簡(jiǎn)單的項(xiàng)目,所以沒有用工程結(jié)構(gòu)去實(shí)現(xiàn)它,這里介紹下它們之間的關(guān)系。
3、資源文件分析
本項(xiàng)目的所有音樂文件都是存放在本地的,沒有用服務(wù)器,當(dāng)然也可以用,參考我的多媒體播放器:Android Studio實(shí)現(xiàn)多媒體播放器。
我在res文件夾下創(chuàng)建了raw文件夾,用來放音樂文件,音樂文件的命名注意是從music0開始,很多同學(xué)從music1開始命名導(dǎo)致獲取不到第一首音樂然后閃退。
在drawable文件夾存放了音樂專輯圖片bg.jpg、播放器背景圖片music_bg.jpg,歌手圓形圖片music0.png、music1.png等,還有按鈕圖片play.png、pause.png等。
三、開發(fā)環(huán)境
四、優(yōu)化設(shè)計(jì)
1、上一首下一首
想實(shí)現(xiàn)跳轉(zhuǎn)到上一首和下一首歌曲的播放界面,肯定修改的是MusicActivity。
1.1、可以這樣考慮:如果每次都獲取對(duì)應(yīng)歌曲的intent(意圖)進(jìn)行跳轉(zhuǎn),這樣實(shí)現(xiàn)起來就比較復(fù)雜了??梢钥吹給nCreate方法里面intent1就是獲取到的歌曲列表界面跳轉(zhuǎn)到音樂播放界面的意圖。我們換個(gè)巧妙的方法,利用這個(gè)intent1來實(shí)現(xiàn)。
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //綁定布局文件 setContentView(R.layout.activity_music); //獲得意圖 intent1=getIntent(); //初始化 init(); }
在frag1的列表點(diǎn)擊事件里面,intent用鍵值對(duì)存儲(chǔ)了歌曲名name[position]和下標(biāo)position,分別存儲(chǔ)到“name”和"position"這兩個(gè)鍵中,然后傳給MusicActivity。所以,這個(gè)position就是我們需要利用的。
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { //創(chuàng)建Intent對(duì)象,啟動(dòng)音樂播放界面 Intent intent=new Intent(frag1.this.getContext(), MusicActivity.class); //將數(shù)據(jù)存入Intent對(duì)象,利用鍵值對(duì) intent.putExtra("name",name[position]); intent.putExtra("position",String.valueOf(position)); //開啟意圖 startActivity(intent); } });
1.2、我們首先聲明上一首、下一首這些按鈕變量,然后綁定控件,設(shè)置監(jiān)聽器,修改下布局文件,這些大家肯定都很熟悉,所以這里也不再贅述了。
聲明變量是在onCreate()的上面
private Button play;//播放按鈕 private Button pause;//暫停按鈕 private Button con;//繼續(xù)播放按鈕 private Button pre;//上一首按鈕 private Button next;//下一首按鈕 private Button exit;//退出按鈕 private ImageView iv_music;//歌手圖片框
綁定以及設(shè)置監(jiān)聽器是在init( )中
//依次綁定控件 play=findViewById(R.id.btn_play); pause=findViewById(R.id.btn_pause); con=findViewById(R.id.btn_continue_play); pre=findViewById(R.id.btn_pre); next=findViewById(R.id.btn_next); exit=findViewById(R.id.btn_exit); //依次設(shè)置監(jiān)聽器 play.setOnClickListener(this); pause.setOnClickListener(this); con.setOnClickListener(this); pre.setOnClickListener(this); next.setOnClickListener(this); exit.setOnClickListener(this);
完整的activity_music布局文件代碼會(huì)在后面給出。
1.3、下面就是重寫onClick()方法了,首先intent1是從歌曲列表界面跳轉(zhuǎn)過來的意圖。它傳遞過來歌曲下標(biāo)position,因?yàn)橛胓etStringExtra()方法獲取的是字符串,所以用parseInt()轉(zhuǎn)成整數(shù)i,這樣就獲取到這首歌的下標(biāo)了。
//定義歌曲列表傳過來的下標(biāo)position String position= intent1.getStringExtra("position"); //將字符串轉(zhuǎn)化為整型i int i=parseInt(position);
1.4、iv_music就是圖片框,顯示歌手的圖片,name_song是歌曲名的文本框,顯示歌曲名稱。這里我在MusicActivity里面定義了一個(gè)musicName的數(shù)組,數(shù)組存放的就是歌曲名,和我們?cè)趂rag1里面定義的name數(shù)組內(nèi)容是一樣的。其實(shí)這樣我們就不需要?jiǎng)倓偟膄rag1傳值給我們歌曲名了。
1.5、這時(shí)候我們來獲取上一首歌,只需要i-1就可以了,但是問題來了,這樣只能進(jìn)行一次,繼續(xù)下一首就會(huì)沒有反應(yīng),因?yàn)閛nClick方法是每次被點(diǎn)擊都要調(diào)用的。里面的position每次都初始化為那個(gè)i,所以你改變下標(biāo)只能改變一次而已。
1.6、既然這樣行不通,那么如何做到每次更新這個(gè)下標(biāo)呢,這里可以自己定義個(gè)全局變量change,用來記錄這個(gè)下標(biāo),每次加一或者減一,這個(gè)變量是全局變量,它的值是可以變化的,所以每次調(diào)用onClick()方法并不會(huì)復(fù)原值。
public int change=0;//記錄下標(biāo)的變化值
1.7、這樣,不管是上一首i-1還是下一首i+1都可以直接再加上change就行了,代碼如下:
case R.id.btn_pre://播放上一首 if((i+change)<1) { Toast.makeText(MusicActivity.this, "已經(jīng)是第一首了", Toast.LENGTH_SHORT).show(); return; } else { change--; music_pic.setImageResource(frag1.icons[i+change]); name_song.setText(musicName[i+change]); musicControl.play(i+change); animator.start(); break; } case R.id.btn_next://播放下一首 if((i+change)==musicName.length-1) {//這里musicName.length-1表示的最后一首歌的下標(biāo) Toast.makeText(MusicActivity.this, "已經(jīng)是最后一首了", Toast.LENGTH_SHORT).show(); return; } else { change++; music_pic.setImageResource(frag1.icons[i+change]); name_song.setText(musicName[i+change]); musicControl.play(i+change); animator.start(); break; }
1.8、這樣在第一版基礎(chǔ)上可以成功實(shí)現(xiàn)上一首下一首的效果:
完整的MusicActivity代碼如下:
package zj.dzh.music_list; import androidx.annotation.RequiresApi; import androidx.appcompat.app.AppCompatActivity; import android.animation.ObjectAnimator; import android.content.ComponentName; import android.content.Intent; import android.content.ServiceConnection; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.SystemClock; import android.view.View; import android.view.animation.LinearInterpolator; import android.widget.Button; import android.widget.ImageView; import android.widget.SeekBar; import android.widget.TextView; import static java.lang.Integer.parseInt; @RequiresApi(api = Build.VERSION_CODES.KITKAT) public class MusicActivity extends AppCompatActivity implements View.OnClickListener{ //定義歌曲名稱的數(shù)組 public String[] musicName={"鄧紫棋——光年之外","蔡健雅——紅色高跟鞋","Taylor Swift——Love Story", "樸樹——平凡之路","田馥甄——小幸運(yùn)","周杰倫——七里香","林俊杰——江南"}; private static SeekBar sb;//定義進(jìn)度條 private static TextView tv_progress,tv_total,name_song;//定義開始和總時(shí)長(zhǎng),歌曲名控件 private ObjectAnimator animator;//定義旋轉(zhuǎn)的動(dòng)畫 private MusicService.MusicControl musicControl;//音樂控制類 private Button play;//播放按鈕 private Button pause;//暫停按鈕 private Button con;//繼續(xù)播放按鈕 private Button pre;//上一首按鈕 private Button next;//下一首按鈕 private Button exit;//退出按鈕 private ImageView iv_music;//歌手圖片框 Intent intent1,intent2;//定義兩個(gè)意圖 MyServiceConn conn; private boolean isUnbind =false;//記錄服務(wù)是否被解綁 public int change=0;//記錄下標(biāo)的變化值 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //綁定布局文件 setContentView(R.layout.activity_music); //獲得意圖 intent1=getIntent(); //初始化 init(); } //初始化 private void init(){ //依次綁定控件 tv_progress=findViewById(R.id.tv_progress); tv_total=findViewById(R.id.tv_total); sb=findViewById(R.id.sb); name_song=findViewById(R.id.song_name); iv_music=findViewById(R.id.iv_music); play=findViewById(R.id.btn_play); pause=findViewById(R.id.btn_pause); con=findViewById(R.id.btn_continue_play); pre=findViewById(R.id.btn_pre); next=findViewById(R.id.btn_next); exit=findViewById(R.id.btn_exit); //依次設(shè)置監(jiān)聽器 play.setOnClickListener(this); pause.setOnClickListener(this); con.setOnClickListener(this); pre.setOnClickListener(this); next.setOnClickListener(this); exit.setOnClickListener(this); //創(chuàng)建意圖對(duì)象 intent2=new Intent(this,MusicService.class); conn=new MyServiceConn();//創(chuàng)建服務(wù)連接對(duì)象 bindService(intent2,conn,BIND_AUTO_CREATE);//綁定服務(wù) //從歌曲列表傳過來的歌曲名 String name=intent1.getStringExtra("name"); //設(shè)置歌曲名顯示 name_song.setText(name); //定義歌曲列表傳過來的下標(biāo)position String position= intent1.getStringExtra("position"); //將字符串轉(zhuǎn)化為整型i int i=parseInt(position); //圖像框設(shè)置為frag1里面的圖標(biāo)數(shù)組,下標(biāo)為i iv_music.setImageResource(frag1.icons[i]); //為滑動(dòng)條添加事件監(jiān)聽 sb.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @RequiresApi(api = Build.VERSION_CODES.KITKAT) @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { //當(dāng)滑動(dòng)條到末端時(shí),自動(dòng)播放下一首 if (progress==seekBar.getMax()){ change++;//自動(dòng)下一首,下標(biāo)加一 String position= intent1.getStringExtra("position"); int nowPosition=(parseInt(position)+change)%musicName.length;//當(dāng)前歌曲下標(biāo) iv_music.setImageResource(frag1.icons[nowPosition]); name_song.setText(musicName[nowPosition]); // musicControl.play(nowPosition);//播放歌曲文件 // musicControl.seekTo(0); // pause.setVisibility(View.VISIBLE); //musicControl.seekTo(0);//重置播放進(jìn)度 } } @Override public void onStartTrackingTouch(SeekBar seekBar) {//滑動(dòng)條開始滑動(dòng)時(shí)調(diào)用 } @Override public void onStopTrackingTouch(SeekBar seekBar) {//滑動(dòng)條停止滑動(dòng)時(shí)調(diào)用 //根據(jù)拖動(dòng)的進(jìn)度改變音樂播放進(jìn)度 int progress=seekBar.getProgress();//獲取seekBar的進(jìn)度 musicControl.seekTo(progress);//改變播放進(jìn)度 } }); animator=ObjectAnimator.ofFloat(iv_music,"rotation",0f,360.0f); animator.setDuration(10000);//動(dòng)畫旋轉(zhuǎn)一周的時(shí)間為10秒 animator.setInterpolator(new LinearInterpolator());//勻速 animator.setRepeatCount(-1);//-1表示設(shè)置動(dòng)畫無限循環(huán) } public static Handler handler=new Handler(){//創(chuàng)建消息處理器對(duì)象 //在主線程中處理從子線程發(fā)送過來的消息 @Override public void handleMessage(Message msg){ Bundle bundle=msg.getData();//獲取從子線程發(fā)送過來的音樂播放進(jìn)度 int duration=bundle.getInt("duration"); int currentPosition=bundle.getInt("currentPosition"); sb.setMax(duration); sb.setProgress(currentPosition); //歌曲總時(shí)長(zhǎng),單位為毫秒 int minute=duration/1000/60; int second=duration/1000%60; String strMinute=null; String strSecond=null; if(minute<10){//如果歌曲的時(shí)間中的分鐘小于10 strMinute="0"+minute;//在分鐘的前面加一個(gè)0 }else{ strMinute=minute+""; } if (second<10){//如果歌曲中的秒鐘小于10 strSecond="0"+second;//在秒鐘前面加一個(gè)0 }else{ strSecond=second+""; } tv_total.setText(strMinute+":"+strSecond); //歌曲當(dāng)前播放時(shí)長(zhǎng) minute=currentPosition/1000/60; second=currentPosition/1000%60; if(minute<10){//如果歌曲的時(shí)間中的分鐘小于10 strMinute="0"+minute;//在分鐘的前面加一個(gè)0 }else{ strMinute=minute+" "; } if (second<10){//如果歌曲中的秒鐘小于10 strSecond="0"+second;//在秒鐘前面加一個(gè)0 }else{ strSecond=second+" "; } tv_progress.setText(strMinute+":"+strSecond); } }; class MyServiceConn implements ServiceConnection{//用于實(shí)現(xiàn)連接服務(wù) @Override public void onServiceConnected(ComponentName name, IBinder service){ musicControl=(MusicService.MusicControl) service; } @Override public void onServiceDisconnected(ComponentName name){ } } //未解綁則解綁 private void unbind(boolean isUnbind){ if(!isUnbind){//判斷服務(wù)是否被解綁 musicControl.pausePlay();//暫停播放音樂 unbindService(conn);//解綁服務(wù) } } @RequiresApi(api = Build.VERSION_CODES.KITKAT) @Override public void onClick(View v) { //獲取歌曲名的下標(biāo)字符串 String position=intent1.getStringExtra("position"); //將字符串轉(zhuǎn)為整數(shù) int i=parseInt(position); switch (v.getId()){ case R.id.btn_play://播放按鈕點(diǎn)擊事件 play.setVisibility(View.INVISIBLE); musicControl.play(i); animator.start(); break; case R.id.btn_pre://播放上一首 if((i+change)<1) { change=musicName.length-1-i; musicControl.play(i+change); iv_music.setImageResource(frag1.icons[i+change]); name_song.setText(musicName[i+change]); pause.setVisibility(View.VISIBLE); animator.start(); break; } else { change--; iv_music.setImageResource(frag1.icons[i+change]); name_song.setText(musicName[i+change]); musicControl.play(i+change); pause.setVisibility(View.VISIBLE); animator.start(); break; } case R.id.btn_next://播放下一首 if((i+change)==musicName.length-1) {//這里musicName.length-1表示的最后一首歌的下標(biāo),即歌曲總數(shù)-1 change=-i; musicControl.play(i+change); iv_music.setImageResource(frag1.icons[i+change]); name_song.setText(musicName[i+change]); pause.setVisibility(View.VISIBLE); animator.start(); break; } else { change++; iv_music.setImageResource(frag1.icons[i+change]); name_song.setText(musicName[i+change]); musicControl.play(i+change); pause.setVisibility(View.VISIBLE); animator.start(); break; } case R.id.btn_pause://暫停按鈕點(diǎn)擊事件 pause.setVisibility(View.INVISIBLE); con.setVisibility(View.VISIBLE); musicControl.pausePlay(); animator.pause(); break; case R.id.btn_continue_play://繼續(xù)播放按鈕點(diǎn)擊事件 con.setVisibility(View.INVISIBLE); pause.setVisibility(View.VISIBLE); musicControl.continuePlay(); animator.start(); break; case R.id.btn_exit://退出按鈕點(diǎn)擊事件 unbind(isUnbind); isUnbind=true; finish(); break; } } @Override protected void onDestroy(){ super.onDestroy(); unbind(isUnbind);//解綁服務(wù) } }
2、個(gè)性化按鈕
2.1、音樂播放器按鈕用這種框框看起來屬實(shí)有些別扭,所以我又花了幾個(gè)小時(shí)從各大圖標(biāo)網(wǎng)站找到了一款比較中意的UI圖。
2.2、通過WPS圖片剪切,將它們裁剪成矢量圖(其實(shí)就是剪成圓形),效果如下圖所示:
2.3、然后修改layout文件,這里要將播放按鈕、暫停按鈕、繼續(xù)播放按鈕三個(gè)按鈕進(jìn)行重疊。我通過android:layout_centerHorizontal="true"來將它們?nèi)齻€(gè)全部水平居中,這樣就重疊在一起了,但是重疊順序也必須有講究,最上層的是播放按鈕(btn_play),第二層是暫停按鈕(btn_pause),第三層是
繼續(xù)播放按鈕(btn_continue_play)。
同學(xué)甲:為什么最上層的是播放按鈕?
博主:因?yàn)椴シ虐粹o點(diǎn)擊之后要讓它消失,所以它必須放第一個(gè)。
同學(xué)乙:為什么第二層的是暫停按鈕?
博主:因?yàn)椴シ鸥枨罂隙ㄒ軙和8枨砸茱@示暫停按鈕,而且暫停按鈕點(diǎn)擊之后會(huì)消失,顯示最底下的繼續(xù)播放按鈕。
同學(xué)丙:那我按下繼續(xù)播放按鈕是不是歌曲繼續(xù)播放,繼續(xù)播放按鈕消失,然后暫停按鈕出現(xiàn)?
博主:你說的沒錯(cuò),就是這樣的邏輯。
這里給出完整的layout代碼:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout 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" android:background="@drawable/music_bg" tools:context=".MusicActivity" android:gravity="center" android:orientation="vertical"> <ImageView android:id="@+id/iv_music" android:layout_width="240dp" android:layout_height="240dp" android:layout_gravity="center_horizontal" android:layout_margin="15dp" android:src="@drawable/music0"/> <TextView android:id="@+id/song_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="歌曲名" android:textSize="20sp"/> <SeekBar android:id="@+id/sb" android:layout_width="match_parent" android:layout_height="wrap_content" /> <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingLeft="8dp" android:paddingRight="8dp"> <TextView android:id="@+id/tv_progress" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="00:00"/> <TextView android:id="@+id/tv_total" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:text="00:00"/> </RelativeLayout> <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <Button android:id="@+id/btn_continue_play" android:layout_width="80dp" android:layout_height="80dp" android:layout_centerHorizontal="true" android:background="@drawable/play"/> <Button android:id="@+id/btn_pause" android:layout_width="80dp" android:layout_height="80dp" android:layout_centerHorizontal="true" android:background="@drawable/pause"/> <Button android:id="@+id/btn_play" android:layout_width="80dp" android:layout_height="80dp" android:layout_centerHorizontal="true" android:background="@drawable/play" /> <Button android:id="@+id/btn_pre" android:layout_width="60dp" android:layout_height="60dp" android:background="@drawable/pre" android:layout_centerVertical="true" android:layout_marginRight="10dp" android:layout_toLeftOf="@id/btn_play"/> <Button android:id="@+id/btn_next" android:layout_width="60dp" android:layout_height="60dp" android:background="@drawable/next" android:layout_centerVertical="true" android:layout_marginLeft="10dp" android:layout_toRightOf="@id/btn_play"/> <Button android:id="@+id/btn_exit" android:layout_width="60dp" android:layout_height="60dp" android:background="@drawable/exit" android:layout_centerVertical="true" android:layout_marginLeft="30dp" android:layout_toRightOf="@id/btn_next"/> </RelativeLayout> </LinearLayout>
五、運(yùn)行效果
1、打開項(xiàng)目,編譯成功后運(yùn)行,會(huì)看到Android Studio執(zhí)行這樣的過程:Starting AVD啟動(dòng)模擬器,然后gradle build構(gòu)建項(xiàng)目,接著就會(huì)install安裝app,最后launch運(yùn)行出app。首先顯示的是歌曲列表界面:
2、我們選擇【平凡之路】進(jìn)行播放,跳轉(zhuǎn)到音樂播放界面:
3、點(diǎn)擊【播放】按鈕,音樂開始播放,圖片開始轉(zhuǎn)動(dòng):
4、拖動(dòng)進(jìn)度條,音樂直接快進(jìn)到指定位置:
5、點(diǎn)擊【下一首】按鈕,播放了【小幸運(yùn)】:
6、再點(diǎn)擊【下一首】按鈕,播放了【七里香】:
7、點(diǎn)擊【上一首】按鈕,又播放了【小幸運(yùn)】:
8、點(diǎn)擊【暫?!堪粹o,歌曲停止播放,圖片也停止旋轉(zhuǎn):
9、點(diǎn)擊繼續(xù)播放按鈕,歌曲繼續(xù)播放,圖片繼續(xù)旋轉(zhuǎn):
10、點(diǎn)擊【退出】按鈕,返回到歌曲列表界面:
11、點(diǎn)擊【專輯】選項(xiàng),進(jìn)入到專輯界面,依舊選的是老霉的《Red》專輯封面
六、項(xiàng)目總結(jié)
本次項(xiàng)目主要是對(duì)上一個(gè)版本的音樂播放器進(jìn)行功能和UI上的優(yōu)化,實(shí)現(xiàn)起來并不算復(fù)雜但是有些細(xì)節(jié)需要注意,不過有耐心就可以了。其實(shí)不管遇到什么問題,都應(yīng)該耐心去分析問題,然后想出解決思路,再然后解決問題。這個(gè)過程可能會(huì)很短暫也可能會(huì)很漫長(zhǎng),但是我們要堅(jiān)信只要你用心去做,肯定會(huì)解決的。
到此這篇關(guān)于Android Studio實(shí)現(xiàn)音樂播放器2.0的文章就介紹到這了,更多相關(guān)Android Studio實(shí)現(xiàn)音樂播放器2.0內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android實(shí)現(xiàn)自動(dòng)填寫獲取驗(yàn)證碼功能
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)自動(dòng)填寫獲取驗(yàn)證碼功能,感興趣的小伙伴們可以參考一下2016-03-03Android?8.0實(shí)現(xiàn)藍(lán)牙遙控器自動(dòng)配對(duì)
這篇文章主要為大家詳細(xì)介紹了Android?8.0實(shí)現(xiàn)藍(lán)牙遙控器自動(dòng)配對(duì),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-08-08詳解Android數(shù)據(jù)存儲(chǔ)之Android 6.0運(yùn)行時(shí)權(quán)限下文件存儲(chǔ)的思考
本篇文章主要介紹了Android數(shù)據(jù)存儲(chǔ)之Android 6.0運(yùn)行時(shí)權(quán)限下文件存儲(chǔ)的思考,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。2016-12-12Android中Retrofit+OkHttp進(jìn)行HTTP網(wǎng)絡(luò)編程的使用指南
Retrofit和OkHttp都是Square在GitHub上開源的第三方HTTP支持包,兩個(gè)包可以搭配使用,本文即是來講解Android中Retrofit+OkHttp進(jìn)行HTTP網(wǎng)絡(luò)編程的使用指南:2016-07-07react native打包apk文件安裝好之后進(jìn)入應(yīng)用閃退的解決方案
這篇文章主要介紹了react native打包apk文件安裝好之后進(jìn)入應(yīng)用閃退的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-09-09Android Studio實(shí)現(xiàn)簡(jiǎn)易進(jìn)制轉(zhuǎn)換計(jì)算器
這篇文章主要為大家詳細(xì)介紹了Android Studio實(shí)現(xiàn)簡(jiǎn)易進(jìn)制轉(zhuǎn)換計(jì)算器,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-05-05如何為RecyclerView添加Header和Footer
這篇文章主要為大家詳細(xì)介紹了如何為RecyclerView添加Header和Footer,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-12-12Android下2d物理引擎Box2d用法簡(jiǎn)單實(shí)例
這篇文章主要介紹了Android下2d物理引擎Box2d用法,實(shí)例分析了在Android平臺(tái)上使用Box2d的基本技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-07-07