Kotlin項目實戰之手機影音---主界面tab切換、home界面適配、獲得首頁網絡數據


主界面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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM