簡易AndroidMusicBox實現(xiàn)

app下載鏈接,歡迎給我的所謂的app找bug面褐,謝謝指教拌禾!聽朋友反映,問題還不少展哭,有的手機根本無法打開app,有的出現(xiàn)歌曲列表就閃退湃窍,正在努力debug,見諒,浪費各位流量了匪傍!
http://pan.baidu.com/s/1jHNa5Bg

前言

MusicBox的制作過程涉及到不少知識您市,如四大組件、MediaStore等等役衡。制作完成后收獲頗豐茵休,現(xiàn)在帶大家制作自己的音樂播放器。

開發(fā)思路

程序由一個Activity(交互)和Service(后臺播放音樂)構(gòu)成手蝎。當發(fā)生單擊事件時榕莺,Activity發(fā)送廣播通知Service改變播放狀態(tài);Service將當前播放狀態(tài)和當前播放的是哪一首歌曲廣播給Activity供其更新UI界面棵介。而歌曲信息(歌名帽撑、藝術(shù)家、路徑等)則由系統(tǒng)的多媒體ContentProvider提供鞍时。

musicBox開發(fā)思路圖

開發(fā)細節(jié)

1.布局界面有兩個TextView及5個ImageButton(分別代表上一首亏拉、播放/暫停扣蜻、下一首、停止及塘、顯示歌曲列表).見文末效果展示圖.5個Button均注冊監(jiān)聽器莽使,當單擊事件發(fā)生時向Service發(fā)送廣播。為了讓Service知道哪個按鈕被按下了笙僚,可以這樣操作:

intent.putExra("contral",xxx);  //xxx是Button的flag芳肌,比如
//“上一首”這個Button的flag設置為0x123
sendBrocast(intent);

2.Activity創(chuàng)建三個String數(shù)組,分別存儲歌曲的title,artist肋层,display_name,而Service中則創(chuàng)建一個String數(shù)組亿笤,用于存儲歌曲的path《安可以做到4個數(shù)組的同一下標對應同一首歌曲净薛。這樣做的好處是Activity和Service之間只需要交換數(shù)組的下標就可以實現(xiàn)TextView的更新以及歌曲的切換,避免了直接傳輸字符串蒲拉。

3.Service設置一個status標志播放狀態(tài)肃拜,并廣播給Activity。Activity依據(jù)此改變表示播放/暫停的那個ImageButton顯示的圖片雌团。

4.當用戶點擊用于顯示歌曲列表的Button時燃领,可以使用AlertDialog加載一個單選列表對話框,這相比跳轉(zhuǎn)到另一Activity的思路更加簡單易行锦援。要使用戶點擊列表的某一首歌時能切歌并使列表消失猛蔽,只需覆寫 DialogInterface.OnClickListener的onClick方法即可,如下:

DialogInterface.OnClickListener() {
                        @Override
                            public void onClick(DialogInterface dialog, int which) {
                            Intent intent=new Intent(actionByActivity);//actionByActivity是自定義的action
                            intent.putExtra("contral",which);//which從0開始灵寺,表示第which+1首歌被點擊
                            sendBroadcast(intent);
                           dialog.dismiss();//使dialog消失
                        }
                    }

5.Service使用MediaPlayer播放音樂枢舶,為了使在無用戶交互時能順序播放,為MediaPlayer對象注冊播放完畢監(jiān)聽器替久,如下:

 //player為MediaPlayer對象
 player.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
        @Override
        public void onCompletion(MediaPlayer mp) {
            player.reset();
            try {
                current=(current+1)%(num-1);//current是正在播放的歌曲的數(shù)組下標
                player.setDataSource(path[current]);//path[]為路徑數(shù)組
                player.prepare();
                player.start();
            }catch (IOException e){
                e.printStackTrace();
            }
            Intent intent=new Intent(MainActivity.actionByService);
            intent.putExtra("current",current);
            sendBroadcast(intent);//把current廣播給Activity
        }
    });

重點知識講解

MediaStore獲取歌曲信息

類MediaStore可以看作是安卓Media數(shù)據(jù)庫的配置說明(說明該數(shù)據(jù)庫表名、列名躏尉、訪問的uri等等)蚯根,MediaStore這個類里面包含了ImageAudio胀糜、Video這幾個內(nèi)部類颅拦。其中類Audio中有以下表名:MediaGenres教藻、Playlists距帅、ArtistsAlbums括堤,每一個表都有其供外部訪問的uri和許多String類型的列名碌秸。顧名思義绍移,要找專輯信息應當在Albums表找,其他類似讥电。為了有個直觀印象蹂窖,看一下Media表的源碼:

Media表源碼1
Media表源碼2
AudioColums
AudioColums

我們看到AudioColums繼承了MediaColums,后者有title、display_name等列名恩敌,如圖:

MediaColums
MediaColums

必備知識

1.AlertDialog創(chuàng)建對話框知識
2.SQLite數(shù)據(jù)庫知識以及通過Cursor對象訪問的知識
掌握了以上內(nèi)容看起源碼才能不費力氣瞬测,鑒于篇幅限制,讀者自行補充

源碼

MainActivity.java
-------------------------------------------------------------------------------------
package com.golfer.www.musicboxdemo;

import android.app.AlertDialog;
import android.content.DialogInterface;
import android.support.v7.app.AppCompatActivity;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.Cursor;
import android.media.MediaScannerConnection;
import android.provider.MediaStore;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageButton;
import android.widget.RelativeLayout;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity implements View.OnClickListener{
//最大歌曲數(shù)目
final static int  max_num=500;
//實際歌曲數(shù)目
int real_num=0;
//存儲歌曲信息的數(shù)組
private String[] Artist=new String[max_num];
private String[] songname=new String[max_num];
private String[] display_name=new String[max_num];
//當前播放的歌曲的下標
int current=0;
//用于注冊BrocastReceiver的action
/**
在Activity注冊BrocastReceiver用到actionByService
在Service注冊BrocastReceiver用到actionByActivity纠炮,完全可以將actionByActivity在myService.java里面定義
**/
static final String actionByActivity="MusicBoxDemo.action.actionByActivity";
static final String actionByService="MusicBoxDemo.action.actionByService";
//控件
TextView nameOfsong,artist;
ImageButton stop,playOrpause,previous,next,menu;
//BrocastReceiver
ActivityReceiver receiver;
//Intent
Intent intent;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    //匹配控件
    nameOfsong=(TextView)findViewById(R.id.nameOfsong) ;
    artist=(TextView)findViewById(R.id.artist) ;
    previous=(ImageButton)findViewById(R.id.previous);
    playOrpause=(ImageButton)findViewById(R.id.playOrpause);
    stop=(ImageButton)findViewById(R.id.stop) ;
    next=(ImageButton)findViewById(R.id.next) ;
    menu=(ImageButton)findViewById(R.id.menu) ;
    //為按鈕注冊監(jiān)聽器
    previous.setOnClickListener(this);
    playOrpause.setOnClickListener(this);
    stop.setOnClickListener(this);
    next.setOnClickListener(this);
    menu.setOnClickListener(this);
    //讀取MediaDataBase的信息(為數(shù)組賦值)
    prepareInformation();
    //注冊BrocastReceiver
    IntentFilter filter=new IntentFilter();
    filter.addAction(actionByService);
    receiver=new ActivityReceiver();
    registerReceiver(receiver,filter);
    //啟動Service
    this.intent=new Intent(MainActivity.this,myService.class);
    startService(intent);
}

@Override
protected void onDestroy() {
    //取消對BrocastReceiver的注冊
    unregisterReceiver(receiver);
    //停止myService
    stopService(this.intent);
    super.onDestroy();
    return;
}

//自定義BrocastReceiver,用于處理Service發(fā)過來的廣播
private class ActivityReceiver extends BroadcastReceiver{
    @Override
        //覆寫onReceive
    public void onReceive(Context context, Intent intent) {
        //update獲取當前MediaPlayer的播放狀態(tài)
        int update=intent.getIntExtra("update",-1);
        //current獲取當前播放歌曲
        int current=intent.getIntExtra("current",-1);
        //更新文本框內(nèi)容
        if(current>=0){
            nameOfsong.setText(songname[current]);
            artist.setText(Artist[current]);
        }
        //更新按鈕背景
        switch(update){
            case 0x11:
                playOrpause.setImageResource(android.R.drawable.ic_media_pause);
                break;
            case 0x12:
                playOrpause.setImageResource(android.R.drawable.ic_media_play);
                break;
            case 0x13:
                playOrpause.setImageResource(android.R.drawable.ic_media_pause);
                break;
        }
    }
}
//覆寫onClick
public void onClick(View source){
    final Intent intent=new Intent(actionByActivity);
    //根據(jù)source的ID為intent的Extra屬性賦不同的值
    switch(source.getId()){
        case R.id.previous:
            intent.putExtra("contral",0x124);
            break;
        case R.id.playOrpause:
            intent.putExtra("contral",0x125);
            break;
        case R.id.stop:
            intent.putExtra("contral",0x126);
            break;
        case R.id.next:
            intent.putExtra("contral",0x127);
            break;
        case R.id.menu:
            //加載單選列表對話框
            final AlertDialog dialog=new AlertDialog.Builder(MainActivity.this).setTitle("歌曲列表").setSingleChoiceItems(display_name, -1,
                    new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            Intent intent=new Intent(actionByActivity);
                            intent.putExtra("contral",which);
                            sendBroadcast(intent);
                            System.out.println("which:"+which);
                           dialog.dismiss();
                        }
                    }).create();
            dialog.show();
    }
    sendBroadcast(intent);
}
 //讀取歌曲信息
 private  void prepareInformation(){
   //創(chuàng)建ContentResolver
   ContentResolver resolver=getContentResolver();
   //讀取藝術(shù)家信息
   Cursor cursor= resolver.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,null,null,null,null );
   int index=0;
   while(cursor.moveToNext()){
       Artist[index++]=cursor.getString(cursor.getColumnIndex(MediaStore.Audio.AudioColumns.ARTIST));
   }
   real_num=index;
   index=0;
   //讀取歌曲名
   Cursor cursor2= resolver.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,null,null,null,null );
   while(cursor2.moveToNext()){
       songname[index++]=cursor2.getString(cursor2.getColumnIndex(MediaStore.MediaColumns.TITLE));
   }
   index=0;
   //讀取歌曲的展示名
   Cursor cursor3= resolver.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,null,null,null,null );
   while(cursor3.moveToNext()){
       display_name[index++]=cursor3.getString(cursor3.getColumnIndex(MediaStore.MediaColumns.TITLE));
   }
   }
}

myService.java

package com.golfer.www.musicboxdemo;

import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.Cursor;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.IBinder;
import android.provider.MediaStore;
import android.support.annotation.Nullable;
import android.support.annotation.StringDef;

import java.io.IOException;

/**
 * Created by Golfer on 2017/2/4.
*/

public class myService extends Service {
String[] path=new String[MainActivity.max_num];
MediaPlayer player;
int current=0;
int status=0x11;
int num;
ServiceReceiver receiver;
public void onCreate() {
    System.out.println("---Service已啟動---");
    
    IntentFilter filter=new IntentFilter();
    filter.addAction(MainActivity.actionByActivity);
    receiver=new ServiceReceiver();
    registerReceiver(receiver,filter);
    
   if((player=new MediaPlayer())!=null) System.out.println("---MediaPlayer創(chuàng)建成功---");
    else System.out.println("---MediaPlayer創(chuàng)建失敗---");
   
    player.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
        @Override
        public void onCompletion(MediaPlayer mp) {
            player.reset();
            try {
                current=(current+1)%(num-1);
                player.setDataSource(path[current]);
                player.prepare();
                player.start();
            }catch (IOException e){
                e.printStackTrace();
            }
            Intent intent=new Intent(MainActivity.actionByService);
            intent.putExtra("current",current);
            sendBroadcast(intent);
        }
    });
    ContentResolver resolver=getContentResolver();
    Cursor cursor= resolver.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,null,null,null,null );
    int index=0;
    while(cursor.moveToNext()){
            path[index]=cursor.getString(cursor.getColumnIndex(MediaStore.MediaColumns.DATA));
            System.out.println("path["+index+"]:"+path[index]);
        index++;
         }
    num=index;
    return;

}


public IBinder onBind(Intent intent) {
    return null;
}

@Override
public void onDestroy() {
    unregisterReceiver(receiver);
    player.release();
    super.onDestroy();
    return;
}

private class ServiceReceiver extends BroadcastReceiver{
    @Override
    public void onReceive(Context context, Intent intent) {
        int contral=intent.getIntExtra("contral",-1);
        if(contral>=0) {
            switch (contral) {
                case 0x124:
                    if (status == 0x12 && current > 0) {
                        player.reset();
                        try {
                            player.setDataSource(path[current - 1]);
                            player.prepare();
                            player.start();
                            System.out.println("---成功播放上一首歌曲---");
                            current = current - 1;
                        } catch (IOException E) {
                            E.printStackTrace();
                        }
                    }
                    break;
                case 0x125:
                    if (status == 0x11) {
                        try {
                            player.reset();
                            player.setDataSource(path[current]);
                            player.prepare();
                            player.start();
                            status = 0x12;
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    } else if (status == 0x12) {
                        player.pause();
                        status = 0x13;
                    } else if (status == 0x13) {
                        player.start();
                        status = 0x12;
                    }
                    break;
                case 0x126:
                    if (status == 0x12 || status == 0x13) {
                        player.stop();
                        status = 0x11;
                    }
                    break;
                case 0x127:
                    if (status == 0x12 && current < num - 1) {
                        player.reset();
                        try {
                            player.setDataSource(path[current + 1]);
                            player.prepare();
                            player.start();
                            System.out.println("---成功播放下一首歌曲---");
                            current = current + 1;
                        } catch (IOException E) {
                            E.printStackTrace();
                        }
                    }
                    break;
                default:
                    player.reset();
                    try {
                        player.setDataSource(path[contral]);
                        player.prepare();
                        player.start();
                        current=contral;
                        System.out.println("---成功切換播放第"+(current+1)+"首歌---");
                    }catch (IOException e){
                        e.printStackTrace();
                    }
            }
        }
        Intent intent1=new Intent(MainActivity.actionByService);
        intent1.putExtra("update",status);
        intent1.putExtra("current",current);
        sendBroadcast(intent1);
    }
}
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.golfer.www.musicboxdemo.MainActivity">

<TextView
    android:text="歌曲名稱"
    android:layout_height="wrap_content"
    android:id="@+id/nameOfsong"
    android:layout_alignParentStart="true"
    android:layout_width="wrap_content"
    android:textAppearance="@style/TextAppearance.AppCompat.Small" />

<ImageButton
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:srcCompat="@android:drawable/ic_media_next"
    android:layout_alignTop="@+id/previous"
    android:layout_alignParentEnd="true"
    android:id="@+id/next" />

<ImageButton
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:srcCompat="@android:drawable/ic_media_previous"
    android:layout_marginBottom="132dp"
    android:id="@+id/previous"
    android:layout_alignParentBottom="true"
    android:layout_alignParentLeft="true"
    android:layout_alignParentStart="true" />

<ImageButton
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:srcCompat="@android:drawable/ic_media_pause"
    android:id="@+id/playOrpause"
    android:layout_alignTop="@+id/next"
    android:layout_centerHorizontal="true" />

<ImageButton
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:srcCompat="@android:drawable/ic_lock_power_off"
    android:id="@+id/stop"
    android:layout_marginTop="29dp"
    android:layout_below="@+id/playOrpause"
    android:layout_toRightOf="@+id/previous"
    android:layout_toEndOf="@+id/previous" />

<TextView
    android:text="歌手"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/artist"
    android:textAppearance="@style/TextAppearance.AppCompat.Small"
    android:layout_marginTop="44dp"
    android:layout_below="@+id/nameOfsong"
    android:layout_alignParentLeft="true"
    android:layout_alignParentStart="true" />

<ImageButton
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:srcCompat="@android:drawable/ic_menu_sort_by_size"
    android:layout_alignBottom="@+id/stop"
    android:layout_toLeftOf="@+id/next"
    android:layout_toStartOf="@+id/next"
    android:layout_marginRight="11dp"
    android:layout_marginEnd="11dp"
    android:id="@+id/menu" />

</RelativeLayout>

AndroidManifest.xml    //記得要在此文件中注冊myService,
//并且為程序添加訪問外部存儲器的權(quán)限月趟,由于兩個BrocastReceiver均使用動態(tài)注冊方式,
//故無須在此文件注冊

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.golfer.www.musicboxdemo">

<application
    android:allowBackup="true"
    android:icon="mipmap/ic_launcher"
    android:label="@string/app_name"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">
    <activity android:name=".MainActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
    <service android:name=".myService">
    </service>
</application>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
</manifest>

效果展示

初始界面
正在播放恢口,第一行中間的按鈕呈三角形
歌曲列表

由于知識水平限制孝宗,app難免存在各種bug,請各位指教弧蝇,或者有其他好的開發(fā)思路碳褒,歡迎共同交流!
app下載鏈接看疗,歡迎給我的所謂的app找bug沙峻,謝謝指教!
http://pan.baidu.com/s/1jHNa5Bg

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末两芳,一起剝皮案震驚了整個濱河市摔寨,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌怖辆,老刑警劉巖是复,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異竖螃,居然都是意外死亡淑廊,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進店門特咆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來季惩,“玉大人,你說我怎么就攤上這事腻格』埃” “怎么了?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵菜职,是天一觀的道長青抛。 經(jīng)常有香客問我,道長酬核,這世上最難降的妖魔是什么蜜另? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任适室,我火速辦了婚禮,結(jié)果婚禮上蚕钦,老公的妹妹穿的比我還像新娘亭病。我一直安慰自己,他們只是感情好嘶居,可當我...
    茶點故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布罪帖。 她就那樣靜靜地躺著,像睡著了一般邮屁。 火紅的嫁衣襯著肌膚如雪整袁。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天佑吝,我揣著相機與錄音坐昙,去河邊找鬼。 笑死芋忿,一個胖子當著我的面吹牛炸客,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播戈钢,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼痹仙,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了殉了?” 一聲冷哼從身側(cè)響起开仰,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎薪铜,沒想到半個月后众弓,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡隔箍,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年谓娃,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蜒滩。...
    茶點故事閱讀 40,102評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡滨达,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出帮掉,到底是詐尸還是另有隱情,我是刑警寧澤窒典,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布蟆炊,位于F島的核電站,受9級特大地震影響瀑志,放射性物質(zhì)發(fā)生泄漏涩搓。R本人自食惡果不足惜污秆,卻給世界環(huán)境...
    茶點故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望昧甘。 院中可真熱鬧良拼,春花似錦、人聲如沸充边。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽浇冰。三九已至贬媒,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間肘习,已是汗流浹背际乘。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留漂佩,地道東北人脖含。 一個月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像投蝉,于是被迫代替她去往敵國和親养葵。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,044評論 2 355

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,167評論 25 707
  • 本人初學Android墓拜,最近做了一個實現(xiàn)安卓簡單音樂播放功能的播放器港柜,收獲不少,于是便記錄下來自己的思路與知識總結(jié)...
    落日柳風閱讀 19,134評論 2 41
  • ¥開啟¥ 【iAPP實現(xiàn)進入界面執(zhí)行逐一顯】 〖2017-08-25 15:22:14〗 《//首先開一個線程咳榜,因...
    小菜c閱讀 6,424評論 0 17
  • 1.什么是Activity?問的不太多涌韩,說點有深度的 四大組件之一,一般的,一個用戶交互界面對應一個activit...
    JoonyLee閱讀 5,734評論 2 51
  • 中午過后畔柔,感覺自己今天好像沒太做什么事情,心里有種失落感和不滿足感臣樱。 看從什么角度講靶擦,從高效能方面,今天比較低效雇毫,...
    榮涵閱讀 170評論 0 0