日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

Android音乐播放器开发(6)—ListView组件创建歌曲播放列表(内含原理分析)

發布時間:2024/1/1 Android 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android音乐播放器开发(6)—ListView组件创建歌曲播放列表(内含原理分析) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1. 說明

源碼已同步到Gitee倉庫,GitHub倉庫,覺得還不錯的話幫忙點個“star”吧,非常感謝。

以往的文章

  • 服務端:Android音樂播放器開發–服務端

  • 登錄:Android音樂播放器開發–登錄

  • 注冊:Android音樂播放器開發–注冊

  • 修改密碼:Android音樂播放器開發–修改密碼

  • 播放:Android音樂播放器開發–播放

適用于平時做個小課設的小伙伴們

本部分內容實現效果如下:

2. 界面設計

界面主體是一個ListView,它以列表的形式展示內容,當數據量足夠多時會出現滾動條,并且能夠根據數據量自適應屏幕;而ListView的元素都是歌曲對應的基本信息。

  • 標題欄
  • 標題欄仿照登錄時介紹的main_title_bar.xml新建了一個表頭文件,命名為title_bar.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="56dp"android:background="@color/colorPrimary"><ImageButtonandroid:id="@+id/ib_title_back"android:src="@drawable/go_back_selector"android:background="@null"android:layout_marginLeft="10dp"android:layout_width="50dp"android:layout_height="50dp"android:layout_alignParentLeft="true"android:layout_centerVertical="true"/><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="播放列表"android:layout_centerVertical="true"android:layout_centerHorizontal="true"android:textColor="#fff"android:textSize="20sp"android:id="@+id/tv_title"/> </RelativeLayout>

  • ListView
  • 這一部分只加了一個ListView組件(activity_music_list.xml)

    <?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:orientation="vertical"tools:context="cn.sjcup.musicplayer.activity.MusicListActivity"><include layout="@layout/title_bar"></include><FrameLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"><ListViewandroid:layout_width="match_parent"android:layout_height="match_parent"android:id="@+id/lv_music" /></FrameLayout></LinearLayout>

  • Item布局
  • 這部分是ListView顯示的元素布局。(activity_item.xml)

    Item顯示了歌曲的基本信息,包括歌曲封面、歌名和演唱信息,另外加了一個標記,用于標識當前所播的歌曲。

    <?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="65dp"><com.loopj.android.image.SmartImageViewandroid:id="@+id/siv_img"android:layout_width="80dp"android:layout_height="60dp"android:layout_alignParentLeft="true"android:layout_marginBottom="5dp"android:layout_marginLeft="5dp"android:layout_marginTop="5dp"android:scaleType="centerCrop"android:src="@mipmap/ic_launcher"></com.loopj.android.image.SmartImageView><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:id="@+id/tv_name"android:layout_marginLeft="5dp"android:layout_marginTop="10dp"android:layout_toRightOf="@id/siv_img"android:ellipsize="end"android:maxLength="20"android:singleLine="true"android:text="歌曲"android:textColor="#000000"android:textSize="18sp"/><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:id="@+id/tv_author"android:layout_below="@id/tv_name"android:layout_marginLeft="5dp"android:layout_marginTop="5dp"android:layout_toRightOf="@id/siv_img"android:ellipsize="end"android:maxLength="16"android:singleLine="true"android:text="作者"android:textColor="#99000000"android:textSize="14sp"/><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:id="@+id/tv_type"android:layout_alignParentBottom="true"android:layout_alignParentRight="true"android:layout_marginBottom="5dp"android:layout_marginRight="10dp"android:text="播放中"android:textColor="#99000000"android:textSize="12sp"/></RelativeLayout>

    3. 功能設計

    3.1定義變量

    //控件 private ImageButton mBack; private ListView mMusicList; private TextView mState;//獲取到的數據 private JSONArray musicList; private JSONObject musicInfo;//獲取工具類實例化對象(這個工具類很重要,在播放界面一文的第五部分提到) private MusicPlayUtil musicPlayUtil = MusicPlayUtil.getInstance();

    3.2 初始化界面

    在初始化時,分為了界面初始化和事件初始化

    @Override protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_music_list);//初始化界面initView();//設置相關事件initEvent(); }

    初始化界面,綁定界面控件,將歌曲信息填充到ListView中

    //初始化界面 private void initView() {mBack = findViewById(R.id.ib_title_back);mMusicList = findViewById(R.id.lv_music);fillData(); //填充數據 }

    數據填充使用setAdapter方法,這是ListView本身自帶的方法,通過名稱可以看出,Adapter意為適配器,我們將適配器填充到ListView中。參數是實例化后的MusicAdapter對象。MusicAdapter對象繼承了BaseAdapter,并重寫了四個方法。

    四個方法分別為:

    • int getCount() 填充的item個數
    • Object getItem(int position) 指定索引對應的item數據項
    • long getItemId(int position) 指定索引對應item的id值
    • View getView(final int position, View convertView, ViewGroup parent) 填充每個item的可視內容并返回

    我們可以簡單的理解為,適配器先從getCount里確定填充的數量,然后循環執行getView方法將條目一個一個繪制出來,所以必須重寫的是getCountgetView方法。而getItemgetItemId是調用某些函數才會觸發的方法,如果不需要使用時可以暫時不修改。

    具體的介紹可以參考—>博客

    下面是填充數據的這一部分整體的程序,稍后會拆開介紹。

    //填充數據 private void fillData() {mMusicList.setAdapter(new MusicAdapter()); }private class MusicAdapter extends BaseAdapter {@Overridepublic int getCount() {return musicPlayUtil.getMusicNum();}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {ViewHolder holder; //ViewHolder是定義的局部定義類,作為信息傳遞的載體musicList = musicPlayUtil.getMusicList();try{musicInfo = musicList.getJSONObject(position);}catch (Exception e){e.printStackTrace();}if (convertView == null){convertView = LayoutInflater.from(getApplicationContext()).inflate(R.layout.activity_item,parent,false);holder = new ViewHolder();holder.siv = convertView.findViewById(R.id.siv_img);holder.tv_name = convertView.findViewById(R.id.tv_name);holder.tv_author = convertView.findViewById(R.id.tv_author);holder.tv_type = convertView.findViewById(R.id.tv_type);convertView.setTag(holder);}else {holder = (ViewHolder) convertView.getTag();}holder.siv.setImageUrl(IMG+musicInfo.optString("img"), R.mipmap.ic_launcher,R.mipmap.ic_launcher);holder.tv_name.setText(musicInfo.optString("name"));holder.tv_author.setText(musicInfo.optString("author"));holder.tv_type.setText("");if (musicPlayUtil.getMusicId()==position){holder.tv_type.setText("播放中");holder.tv_type.setTextColor(Color.RED);mState=holder.tv_type;}return convertView;}@Overridepublic Object getItem(int position) {return null;}@Overridepublic long getItemId(int position) {return 0;}class ViewHolder{TextView tv_name;TextView tv_author;TextView tv_type;SmartImageView siv;} }

    分別講解一下。

    因為需要頻繁的傳遞信息,這里定義了一個局部內部類,分別對應了item布局的4個控件(拓展一下:內部類分為靜態內部類、成員內部類、局部內部類、匿名內部類。感興趣的小伙伴可以去復習一下)

    class ViewHolder{TextView tv_name;TextView tv_author;TextView tv_type;SmartImageView siv; }

    getCount方法,返回的是填充的item的數量,在本項目中,就是歌曲的數量。歌曲數量已經在MainActivity中獲取,這里可以通過工具類(重申一遍,這個工具類在播放界面這篇文章的第五部分有講到)直接獲取到這個值。

    @Override public int getCount() {return musicPlayUtil.getMusicNum(); }

    getView方法,返回的是填充的view信息,這一部分是本文最難理解的。參數position相當于一個標識,用來表示進行操作的是哪一個item,從0開始計數;convertView就是待返回的view信息,初始化時為null,我們要加工的也就是這個對象。

    首先,定義了一個類holder,使用時需要實例化一個ViewHolder(上面定義的局部內部類)作為信息傳遞的載體,也就是填充得item數量決定了ViewHolder的實例化對象的數量。所有歌曲信息可以通過上面定義的工具類獲取。然后通過position獲取到對應的歌曲信息(JSONArray里的object也是從0開始計數的,我們可以認為是一一對應的)。

    下面我們做個測試,檢測一下什么時候調用getView方法。

    ------------------------------------------------------------Test---------------------------------------------------------

    我們在getView方法中輸出position,也就是每次調用getView方法時都會輸出當前的position

    這時我們打開音樂列表,可以看到顯示了11首歌曲

    輸出顯示調用了11次getView方法,position對應了0–>10

    然后向上滑,加載其它的歌曲,上滑的同時,其它內容通過getView方法被加載,position對應11->16

    現在我們想一個問題,此時下滑,也就是希望顯示之前的歌曲信息,getView會被重新調用嗎?(1. 第一種想法當然是認為之前的歌曲信息已經加載出來了,當然不會再調用了;2. 第二種想法就是會調用吧)

    事實是加載之前的信息會重新調用getView

    經過簡單的測試,我們可以得出一個小結論,界面中可以顯示出多少個item,就會調用多少次getView方法加載信息,也就是哪個item進入可是范圍,就會調用getView方法信息。前面介紹道,理論上有多少條信息就應該有對應相應數量的item,但是考慮到手機的內存是有限的,如果數量過多,占用內存就會過大,實際上的操作只會初始化特定數量的item(依屏幕長度而定),在滑動過程中,item重復使用,使用getView方法不斷賦予新的信息。

    ------------------------------------------------------------ENDTest---------------------------------------------------------

    關于setTaggetTag的使用。在setTag之前,convertView需要找到xml中定義的layout,這里是activity_item,相當于綁定了一個目標item,然后使用findViewById綁定item中的組件,再對每個組件賦予不同的值。前面介紹道,上滑又下滑后,前面加載的信息會再次使用getView方法“繪制”一遍,但已經綁定的組件沒有必要再綁定一次,因為這個過程比較消耗資源,因此在一次使用findViewById綁定后,調用setTag保存起來,再次加載時,同于同樣的信息,直接使用getTag獲取到已綁定組件的ViewHolder,而無需重復綁定組件。

    if (convertView == null){convertView = LayoutInflater.from(getApplicationContext()).inflate(R.layout.activity_item,parent,false);holder = new ViewHolder();holder.siv = convertView.findViewById(R.id.siv_img);holder.tv_name = convertView.findViewById(R.id.tv_name);holder.tv_author = convertView.findViewById(R.id.tv_author);holder.tv_type = convertView.findViewById(R.id.tv_type);convertView.setTag(holder); }else {holder = (ViewHolder) convertView.getTag(); }

    對于初學者來說,這個比較難理解,建議多看幾篇文章。以上很多內容都是本人自己的理解,如果有不對的地方敬請指出。

    @Override public View getView(int position, View convertView, ViewGroup parent) {ViewHolder holder; //ViewHolder是定義的局部內部類,作為信息傳遞的載體musicList = musicPlayUtil.getMusicList();try{musicInfo = musicList.getJSONObject(position);}catch (Exception e){e.printStackTrace();}if (convertView == null){convertView = LayoutInflater.from(getApplicationContext()).inflate(R.layout.activity_item,parent,false);holder = new ViewHolder();holder.siv = convertView.findViewById(R.id.siv_img);holder.tv_name = convertView.findViewById(R.id.tv_name);holder.tv_author = convertView.findViewById(R.id.tv_author);holder.tv_type = convertView.findViewById(R.id.tv_type);convertView.setTag(holder);}else {holder = (ViewHolder) convertView.getTag();}holder.siv.setImageUrl(IMG+musicInfo.optString("img"), R.mipmap.ic_launcher,R.mipmap.ic_launcher);holder.tv_name.setText(musicInfo.optString("name"));holder.tv_author.setText(musicInfo.optString("author"));holder.tv_type.setText("");if (musicPlayUtil.getMusicId()==position){holder.tv_type.setText("播放中");holder.tv_type.setTextColor(Color.RED);mState=holder.tv_type;}return convertView; }

    3.3定義事件

    在這個界面,我們希望實現兩個點擊事件,1. 點擊“返回”按鈕,可以返回播放界面;2. 點擊一個歌曲信息,可以播放對應的歌曲。

    //初始化事件 private void initEvent() {mMusicList.setOnItemClickListener(new AdapterView.OnItemClickListener() {@Overridepublic void onItemClick(AdapterView<?> parent, View view, int position, long id) {if (mState == null) {mState = view.findViewById(R.id.tv_type);mState.setText("播放中");mState.setTextColor(Color.RED);musicPlayUtil.setMusicId(position);musicPlayUtil.setMusicView(MainActivity.IsPlay.play);finish();} else {mState.setText("");mState = view.findViewById(R.id.tv_type);mState.setText("播放中");mState.setTextColor(Color.RED);musicPlayUtil.setMusicId(position);musicPlayUtil.setMusicView(MainActivity.IsPlay.play);finish();}}});mBack.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {MusicListActivity.this.finish();}}); }

    第一個事件,返回播放界面比較簡單,這里不再贅述。

    關于第二個事件,定義的變量mState表示的是歌曲的播放狀態,如果在播放狀態,就顯示“播放中”(如下圖),否則不顯示內容。

    第一次點擊時,mState是null,并沒有被賦值,view就是被點擊的item,第一步就是綁定被點擊的item的組件,更改它的內容,響應點擊事件。然后調用工具類中的方法修改歌曲id,這里的position和前面介紹的可以認為是一樣的,由于position從0開始,和我定義的歌曲id是一樣的,這里直接把它當作歌曲id了,如果不一致,這里需要再加一些處理,找到對應的歌曲id,再調用setMusicView播放對應的歌曲。至于finish方法,是做出點擊事件后關閉播放列表界面,返回播放界面,如果你認為沒有必要可以不加。

    mState如果不是null,那么之前是已經綁定過別的item了,修改之前綁定的播放狀態為空字符串,表示沒有播放,再重新綁定新的item,后面內容就與上面相似了,不再贅述。

    4. 測試

    測試成功,放一個測試的動圖

    總結

    以上是生活随笔為你收集整理的Android音乐播放器开发(6)—ListView组件创建歌曲播放列表(内含原理分析)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。