主界面tab切換:
添加點擊事件:
接下來需要處理一下主界面TAB的切換了,這里先添加BottomBar的監聽事件:
class MainActivity : BaseActivity(), ToolBarManager { override fun getLayoutId(): Int { return R.layout.activity_main } override val toolbar by lazy { println("MainActivity layz initMainToolBar()") find<Toolbar>(R.id.toolbar) } override fun initData() { super.initData() initMainToolBar() } override fun initListeners() { super.initListeners() bottomBar.setOnTabSelectListener { when (it) { R.id.tab_home -> toast("首頁") R.id.tab_mv -> toast("MV") R.id.tab_vbang -> toast("v榜") R.id.tab_yuedan -> toast("悅單") } } } }
其中it代表當前選中的是第幾個TAB,運行一下:
准備四個Fragment:
接下來則需要處理Fragment的切換,先准備四個Fragment:
package com.kotlin.musicplayer.ui.fragment import android.graphics.Color import android.view.Gravity import android.view.View import android.widget.TextView import com.kotlin.musicplayer.base.BaseFragment /** * 首頁 */ class HomeFragment : BaseFragment() { override fun initView(): View? { val textView = TextView(context) textView.gravity = Gravity.CENTER textView.setTextColor(Color.RED) textView.text = javaClass.simpleName return textView } }
其中為啥TextView構造時可以直接寫context呢?這里再來復習一下Kotlin的語法:
點擊進去會定位到Fragment的一個方法:
關於這塊的細節可以參考:https://www.cnblogs.com/webor2006/p/11218167.html,好,其它三個依葫蘆畫瓢:
處理Fragment切換顯示:
接下來則來切換Fragment,先來對Fragment的實例的生成封裝一下:
很顯然應該將它設計成一個單例,好,此時對於Kotlin的單例寫法又可以來復習一下了,跟Java寫法一樣,先將構造方法私有化:
在Kotlin中想要將其變為單例就可以使用伴生對象,關於它可以參考:https://www.cnblogs.com/webor2006/p/11210181.html, 如下:
package com.kotlin.musicplayer.utils /** * 管理Fragment的util類 */ class FragmentUtil private constructor() { companion object { val fragmentUtil by lazy { FragmentUtil() } } }
其中by lazy再來復習一下,在之前https://www.cnblogs.com/webor2006/p/12637282.html已經用過一次了:
其中by lazy也是線程安全的,所以就已經是線程安全的單例了,接下來里面封裝一個函數來根據tabId來生成對應的Fragment,如下:
/** * 管理Fragment的util類 */ class FragmentUtil private constructor() { val homeFragment by lazy { HomeFragment() } val mvFragment by lazy { MvFragment() } val vbangFragment by lazy { VBangFragment() } val yueDanFragment by lazy { YueDanFragment() } companion object { val fragmentUtil by lazy { FragmentUtil() } } fun getFragemnt(tabId: Int): BaseFragment? { when (tabId) { R.id.tab_home -> return homeFragment R.id.tab_mv -> return mvFragment R.id.tab_vbang -> return vbangFragment R.id.tab_yuedan -> return yueDanFragment } return null } }
同樣對於每一個Fragment實例的生成也采用by lazy,好接下來則就可以調用了:
報錯了。。看一下啥錯:
其實這個提示不是很准確,確實是由於非空的問題,由於我們封裝的這個getFragment有可能為null,而看一下replace()中的這個參數:
所以此時用alt+enter看一下IDE的修復提示:
試一下第二個:
ok了,這時運行看一下:
那。。這個“!!”是啥語法來着,有點忘了,參考一個這篇https://blog.csdn.net/wuditwj/article/details/84302715所說,它表示如果為空則直接會拋異常,跟?還不一樣。
home界面適配:
准備主布局:
接下來則先來處理HomeFragment, 先來看一下預期的效果長啥樣:
很明顯是一個列表,支持下拉刷新,上拉加載,所以先來准備一下布局:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <androidx.recyclerview.widget.RecyclerView android:id="@+id/recyclerView" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout>
准備列表Item布局:
然后再來准備列表顯示的Item:
然后需要覆寫相印的構造方法,這種有啥可提的,是因為在Kt中要通過IDE的提示來生成構造方法跟Java中操作有些區別,所以拿出來提一下這個細節,直接按alt+enter提示再覆寫不就可以了,是的,看一下是否可以:
完全木有,要覆寫構造得這樣弄:
package com.kotlin.musicplayer.ui.widget import android.content.Context import android.util.AttributeSet import android.widget.RelativeLayout class HomeItemView : RelativeLayout { constructor(context: Context?) : super(context) constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super( context, attrs, defStyleAttr ) }
好,接下來則需要對它進行布局的關聯,這里可以在初始化方法中來寫:
其中item_home就是用的一個卡片布局,布局這塊不多說:
<?xml version="1.0" encoding="utf-8"?> <androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="wrap_content" app:cardCornerRadius="5dp" app:cardElevation="5dp" app:cardUseCompatPadding="true"> <RelativeLayout android:layout_width="match_parent" android:layout_height="300dp" android:padding="10dp"> <ImageView android:id="@+id/img_bg" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@mipmap/default_splash_bg" android:scaleType="centerCrop" /> <ImageView android:id="@+id/image" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_margin="10dp" android:src="@mipmap/home_page_live" /> <TextView android:id="@+id/tv_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_toRightOf="@id/image" android:maxLines="1" android:text="歌單" android:textColor="#00ff00" android:textSize="25sp" /> <TextView android:id="@+id/tv_desc" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_above="@id/tv_title" android:layout_toRightOf="@id/image" android:maxLines="1" android:text="簡介" android:textColor="#fff" android:textSize="20sp" /> </RelativeLayout> </androidx.cardview.widget.CardView>
用到了一張新的資源圖:
在繼續往下編寫之前,這里又得來復習一個知識點,對於Kotlin中的構造分為primary構造方法和secondary構造方法,具體可以參考https://www.cnblogs.com/webor2006/p/11197734.html:回到咱們代碼來:
而對於初始化我們是在init代碼塊中寫的,它是不管主構造還是次構造方法都會調用的,下面用程序來試驗一下:
那,對於主構和次構造來說,還有這么一個區別,就是說在init代碼塊中是可以直接訪問主構造中的參數的,而對於次構造是不能直接訪問,啥意思,看下代碼:
要想訪問次構造的參數,則需要用成員變量給接一下,如下:
創建Adapter:
package com.kotlin.musicplayer.adapter import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import com.kotlin.musicplayer.ui.widget.HomeItemView /** * 首頁列表Adapter */ class HomeAdapter : RecyclerView.Adapter<HomeAdapter.HomeHolder>() { class HomeHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HomeHolder { return HomeHolder(HomeItemView(parent?.context)) } override fun getItemCount(): Int { return 20 } override fun onBindViewHolder(holder: HomeHolder, position: Int) { TODO("Not yet implemented") } }
綁定數據:
在寫HomeFragment綁定數據代碼之前,先來修改一下BaseFragment,有幾個protected方法木有聲明open,在子類中無法重寫:
然后綁定Adapter到RecycleView上,體現一下Kotlin的寫法,明顯比Java的要清爽:
package com.kotlin.musicplayer.ui.fragment import android.view.View import androidx.recyclerview.widget.LinearLayoutManager import com.kotlin.musicplayer.R import com.kotlin.musicplayer.adapter.HomeAdapter import com.kotlin.musicplayer.base.BaseFragment import kotlinx.android.synthetic.main.fragment_home.* /** * 首頁 */ class HomeFragment : BaseFragment() { override fun initView(): View? { return View.inflate(context, R.layout.fragment_home, null) } override fun initListeners() { super.initListeners() recyclerView.layoutManager = LinearLayoutManager(context) val adapter = HomeAdapter() recyclerView.adapter = adapter } }
是不是很神奇,居然木有findViewById直接就可以用recyclerView這個對象。。
而且按ctrl點擊一下則會自動鏈到布局中相應的View上來,如下:
也就是對應在布局中聲明的ViewID,666,連ButterKnife都省了,可見Koltin開發效果其實比用Java是高多了,當然前提是在建立在你會熟練的使用Koltin的前提之下的,所以這也是寫這個項目的目標,好,回到正題,運行看一下效果:
居然報錯了:
定位一下代碼:
這里又是一個跟Java不一樣的地方啦,對於平常我們寫的TODO在Kotlin中如果不去掉是直接會報錯的,意思是這塊就是你忘掉的正常邏輯,所以要想不報錯將此TODO去掉既可:
再運行:
獲得首頁網絡數據:
接下來則來編寫首頁接口的請求了,這里先采用純Okhttp庫來,對於Retrofit的配合在未來再來加入。
添加Okhttp的依賴:
這里集成最新版本:
請求接口:
這里將請求地址封裝到一個工具類中:
package com.kotlin.musicplayer.utils; import android.util.Log; public class URLProviderUtils { /** * 獲取首頁的url * * @param offset 數據偏移量 * @param size 返回數據的條目個數 * @return url */ public static String getHomeUrl(int offset, int size) { String url = "http://tingapi.ting.baidu.com/v1/restserver/ting?from=android&version=5.6.5.0&method=baidu.ting.artist.getSongList&format=json&order=2&tinguid=7988&artistid=7988" + "&offset=" + offset + "&limits=" + size; Log.i("Main_url", url); return url; } }
其中音樂地址是在網上找的,然后接下來請求一下:
其中在Koltin是用object來進行回調處理的,添加網絡權限:
運行:
這里先給個Toast提示一下:
咱們的toast是做UI線程的處理了的:
運行發現請求數據報403了。。
其實這里需要在請求時增加一個user-agent才行,如下:
然后自定義一下Application:
再運行,數據就可以正常的拉取下來了:
另外發現請求了兩遍,其實是BaseFragment封裝得有問題:
這里將其區分一下:
再運行就只會請求一次了:
2020-05-19 15:27:19.474 29816-29816/com.kotlin.musicplayer I/System.out: ToolBarManager.initMainToolBar() 2020-05-19 15:27:19.474 29816-29816/com.kotlin.musicplayer I/System.out: MainActivity layz initMainToolBar() 2020-05-19 15:27:20.214 29816-29889/com.kotlin.musicplayer I/System.out: 獲取數據成功:{"songlist":[{"artist_id":"19165","all_artist_ting_uid":"7988,340525851,340528104,340528105","all_artist_id":"19165,664639518,664750681,664750682","language":"\u82f1\u8bed","publishtime":"2018-12-31","album_no":"16","versions":"","pic_big":"http:\/\/qukufile2.qianqian.com\/data2\/pic\/39006488e5d44cba364d9104c49eedbf\/664751367\/664751367.jpg@s_2,w_150,h_150","pic_small":"http:\/\/qukufile2.qianqian.com\/data2\/pic\/39006488e5d44cba364d9104c49eedbf\/664751367\/664751367.jpg@s_2,w_90,h_90","country":"\u6b27\u7f8e","area":"2","lrclink":"","hot":"0","file_duration":"541","del_status":"0","resource_type":"0","resource_type_ext":"2","copy_type":"1","relate_status":"0","all_rate":"96,224,128,320,flac","has_mv_mobile":0,"toneid":"0","bitrate_fee":"{\"0\":\"129|-1\",\"1\":\"-1|-1\"}","biaoshi":"lossless,vip,perm-1","info":"","has_filmtv":"0","si_proxycompany":"\u770b\u89c1\u7f51\u7edc\u79d1\u6280\uff08\u4e0a\u6d77\uff09\u6709\u9650\u516c\u53f8","song_id":"664750725","title":"The Psychedelic Interlude","ting_uid":"7988","author":"Damage,Deep N Beeper,Deep N Beeper feat. Damage & Bobby Dangler,Bobby Dangler","album_id":"664750684","album_title":"Submerged","is_first_publish":0,"havehigh":2,"charge":0,"has_mv":0,"learn":0,"song_source":"web","piao_id":"0","korean_bb_song":"0","mv_provider":"0000000000","listen_total":"0","pic_radio":"http:\/\/qukufile2.qianqian.com\/data2\/pic\/39006488e5d44cba364d9104c49eedbf\/664751367\/664751367.jpg@s_2,w_300,h_300","pic_s500":"http:\/\/qukufile2.qianqian.com\/data2\/pic\/39006488e5d44cba364d9104c49eedbf\/664751367\/664751367.jpg@s_2,w_500,h_500","pic_premium":"http:\/\/qukufile2.qianqian.com\/data2\/pic\/39006488e5d44cba364d9104c49eedbf\/664751367\/664751367.jpg@s_2,w_500,h_500","pic_huge":"http:\/\/qukufile2.qianqian.com\/data2\/pic\/39006488e5d44cba364d9104c49eedbf\/664751367\/664751367.jpg@s_2,w_1000,h_1000","album_500_500":"http:\/\/qukufile2.qianqian.com\/data2\/pic\/39006488e5d44cba364d9104c49eedbf\/664751367\/664751367.jpg@s_2,w_500,h_500","album_800_800":"","album_1000_1000":"http:\/\/qukufile2.qianqian.com\/data2\/pic\/39006488e5d44cba364d9104c49eedbf\/664751367\/664751367.jpg@s_2,w_1000,h_1000"},{"artist_id":"19165","all_artist_ting_uid":"7988,340525851,340528094,212302,340528100,340528101","all_artist_id":"19165,664639518,664750671,7330727,664750677,664750678","language":"\u82f1\u8bed","publishtime":"2018-12-31","album_no":"6","versions":"","pic_big":"http:\/\/qukufile2.qianqian.com\/data2\/pic\/39006488e5d44cba364d9104c49eedbf\/664751367\/664751367.jpg@s_2,w_150,h_150","pic_small":"http:\/\/qukufile2.qianqian.com\/data2\/pic\/39006488e5d44cba364d9104c49eedbf\/664751367\/664751367.jpg@s_2,w_90,h_90","country":"\u6b27\u7f8e","area":"2","lrclink":"","hot":"0","file_duration":"360","del_status":"0","resource_type":"0","resource_type_ext":"2","copy_type":"1","relate_status":"0","all_rate":"96,224,128,320,flac","has_mv_mobile":0,"toneid":"0","bitrate_fee":"{\"0\":\"129|-1\",\"1\":\"-1|-1\"}","biaoshi":"lossless,vip,perm-1","info":"","has_filmtv":"0","si_proxycompany":"\u770b\u89c1\u7f51\u7edc\u79d1\u6280\uff08\u4e0a\u6d77\uff09\u6709\u9650\u516c\u53f8","song_id":"664750715","title":"Collateral Damage","ting_uid":"7988","author":"Damage,Deep N Beeper,Onepacman,JT,Deep N Beeper feat. Damage, JT, Onepacman & The Paranaut,The Paranaut","album_id":"664750684","album_title":"Submerged","is_first_publish":0,"havehigh":2,"charge":0,"has_mv":0,"learn":0,"song_source":"web","piao_id":"0","korean_bb_song":"0","mv_provider":"0000000000","listen_total":"0","pic_radio":"http:\/\/qukufile2.qianqian.com\/data2\/pic\/39006488e5d44cba364d9104c49eedbf\/664751367\/664751367.jpg@s_2,w_300,h_300","pic_s500":"http:\/\/qukufile2.qianqian.com\/data2\/pic\/39006488e5d44cba364d9104c49eedbf\/664751367\/664751367.jpg@s_2,w_500,h_500","pic_premium":"http:\/\/qukufile2.qianqian.com\/data2\/pic\/39006488e5d44cba364d9104c49eedbf\/664751367\/664751367.jpg@s_2,w_500,h_500","pic_huge":"http:\/\/qukufile2.qianqian.com\/data2\/pic\/39006488e5d44cba364d9104c49eedbf\/6