中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當前位置: 首頁 > news >正文

對政府網(wǎng)站一體化服務建設(shè)的需求百度投放廣告一天多少錢

對政府網(wǎng)站一體化服務建設(shè)的需求,百度投放廣告一天多少錢,網(wǎng)站內(nèi)容包括,深圳網(wǎng)站建設(shè)方案外包Bilibili移動端APP簡介依賴效果登錄效果WebView自定義TobRow的Indicator大小首頁推薦LazyGridView使用Paging3熱門排行榜搜索模糊搜索富文本搜索結(jié)果視頻詳情合集信息Coroutines進行網(wǎng)絡(luò)請求管理,避免回調(diào)地獄添加suspendwithContextGit項目鏈接末簡介 此Demo采用A…

Bilibili移動端APP

  • 簡介
  • 依賴
  • 效果
  • 登錄
    • 效果
    • WebView
  • 自定義TobRow的Indicator大小
  • 首頁
    • 推薦
    • LazyGridView使用Paging3
    • 熱門
  • 排行榜
  • 搜索
    • 模糊搜索
      • 富文本
  • 搜索結(jié)果
  • 視頻詳情
    • 合集
  • 信息
  • Coroutines進行網(wǎng)絡(luò)請求管理,避免回調(diào)地獄
    • 添加suspend
    • withContext
  • Git項目鏈接

簡介

此Demo采用Android Compose聲明式UI編寫而成,主體采用MVVM設(shè)計框架,Demo涉及到的主要技術(shù)包括:Flow、Coroutines、Retrofit、Okhttp、Hilt以及適配了深色模式等;主要數(shù)據(jù)來源于Bilibili API。

依賴

Demo中所使用的依賴如下表格所示

庫名稱備注
Flow
Coroutines協(xié)程
Retrofit網(wǎng)絡(luò)
Okhttp網(wǎng)絡(luò)
Hilt依賴注入
room數(shù)據(jù)存儲
coil異步加載圖片
paging分頁加載
media3-exoplayer視頻

效果

登錄

登錄在Demo中分為WebView嵌入B站網(wǎng)頁實現(xiàn)獲取Cookie和自主實現(xiàn)登錄,由于后者需要通過極驗API驗證,所以暫且采用前者獲取Cookie,后者繪制了基本view和基本邏輯

效果

WebView

由于登錄暫未實現(xiàn),故而此處就介紹使用WebView獲取Cookie。由于在Compose中并未直接提供WebView組件,故使用AndroidView進行引入。以下代碼對WebView進行了一個簡單的封裝,我們只需要在onPageFinished方法中回調(diào)所獲的cookie即可,然后保存到緩存文件即可

@Composable
fun CustomWebView(modifier: Modifier = Modifier,url:String,onBack: (webView: WebView?) -> Unit,onProgressChange: (progress:Int)->Unit = {},initSettings: (webSettings: WebSettings?) -> Unit = {},onReceivedError: (error: WebResourceError?) -> Unit = {},onCookie:(String)->Unit = {}
){val webViewChromeClient = object: WebChromeClient(){override fun onProgressChanged(view: WebView?, newProgress: Int) {//回調(diào)網(wǎng)頁內(nèi)容加載進度onProgressChange(newProgress)super.onProgressChanged(view, newProgress)}}val webViewClient = object: WebViewClient(){override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {super.onPageStarted(view, url, favicon)onProgressChange(-1)}override fun onPageFinished(view: WebView?, url: String?) {super.onPageFinished(view, url)onProgressChange(100)//監(jiān)聽獲取cookieval cookie = CookieManager.getInstance().getCookie(url)cookie?.let{ onCookie(cookie) }}override fun shouldOverrideUrlLoading(view: WebView?,request: WebResourceRequest?): Boolean {if(null == request?.url) return falseval showOverrideUrl = request.url.toString()try {if (!showOverrideUrl.startsWith("http://")&& !showOverrideUrl.startsWith("https://")) {Intent(Intent.ACTION_VIEW, Uri.parse(showOverrideUrl)).apply {addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)view?.context?.applicationContext?.startActivity(this)}return true}}catch (e:Exception){return true}return super.shouldOverrideUrlLoading(view, request)}override fun onReceivedError(view: WebView?,request: WebResourceRequest?,error: WebResourceError?) {super.onReceivedError(view, request, error)onReceivedError(error)}}var webView:WebView? = nullval coroutineScope = rememberCoroutineScope()AndroidView(modifier = modifier,factory = { ctx ->WebView(ctx).apply {this.webViewClient = webViewClientthis.webChromeClient = webViewChromeClientinitSettings(this.settings)webView = thisloadUrl(url)}})BackHandler {coroutineScope.launch {onBack(webView)}}
}

自定義TobRow的Indicator大小

由于在compose中TobRow的指示器寬度被寫死,如果需要更改指示器寬度,則需要自己進行重寫,將源碼拷貝一份,然后根據(jù)自己需求進行定制,具體代碼如下

@ExperimentalPagerApi
fun Modifier.customIndicatorOffset(pagerState: PagerState,tabPositions: List<TabPosition>,width: Dp
): Modifier = composed {if (pagerState.pageCount == 0) return@composed thisval targetIndicatorOffset: Dpval indicatorWidth: Dpval currentTab = tabPositions[minOf(tabPositions.lastIndex, pagerState.currentPage)]val targetPage = pagerState.targetPageval targetTab = tabPositions.getOrNull(targetPage)if (targetTab != null) {val targetDistance = (targetPage - pagerState.currentPage).absoluteValueval fraction = (pagerState.currentPageOffset / max(targetDistance, 1)).absoluteValuetargetIndicatorOffset = lerp(currentTab.left, targetTab.left, fraction)indicatorWidth = lerp(currentTab.width, targetTab.width, fraction).value.absoluteValue.dp} else {targetIndicatorOffset = currentTab.leftindicatorWidth = currentTab.width}fillMaxWidth().wrapContentSize(Alignment.BottomStart).padding(horizontal = (indicatorWidth - width) / 2).offset(x = targetIndicatorOffset).width(width)
}

使用就變得很簡單了,因為是采用modifier的擴展函數(shù)進行編寫,而modifier在每一個compose組件都擁有,所以只需要在tabrow的指示器調(diào)用即可,具體代碼如下

TabRow(...indicator = { pos ->TabRowDefaults.Indicator(color = BilibiliTheme.colors.tabSelect,modifier = Modifier.customIndicatorOffset(pagerState = pageState,tabPositions = pos,32.dp))}...)

首頁

整個首頁頁面由BottomNavbar構(gòu)成,包含四個子界面,其中第一個界面又由兩個子界面組成,通過TabRow+HorizontalPager完成子頁面滑動,子頁面分為推薦熱門兩個頁面

推薦

推薦頁面由上面的Banner和下方的LazyGridView組成,由于Compose中不允許同向滑動,所以就將Banner作為LazyGridView的一個item,進而進行包裹

LazyGridView使用Paging3

由于在現(xiàn)在Compose版本中LazyGridView并不支持Paging3,所以如果有此類需求,則需要自己動手,具體代碼如下

fun <T : Any> LazyGridScope.items(items: LazyPagingItems<T>,key: ((item: T) -> Any)? = null,span: ((item: T) -> GridItemSpan)? = null,contentType: ((item: T) -> Any)? = null,itemContent: @Composable LazyGridItemScope.(value: T?) -> Unit
) {items(count = items.itemCount,key = if (key == null) null else { index ->val item = items.peek(index)if (item == null) {//PagingPlaceholderKey(index)} else {key(item)}},span = if (span == null) null else { index ->val item = items.peek(index)if (item == null) {GridItemSpan(1)} else {span(item)}},contentType = if (contentType == null) {{ null }} else { index ->val item = items.peek(index)if (item == null) {null} else {contentType(item)}}) { index ->itemContent(items[index])}
}

熱門

熱門頁面代碼與推薦頁面代碼類似,此處不在闡述

排行榜

排行界面與上述類似,Tab+HorizontalPager完成所有子頁面滑動切換,此處也不在繼續(xù)闡述

搜索

搜索界面主要分為四個模塊:搜索欄、熱搜內(nèi)容、搜索記錄、搜索列表;搜索框內(nèi)字符改變,搜索列表顯示并以富文本顯示,熱搜內(nèi)容展開與折疊、搜索記錄內(nèi)容展開與折疊、清空記錄等操作都在ViewModel中完成,然后view通過監(jiān)聽VM中狀態(tài)值進行重組

模糊搜索

在搜索框內(nèi)鍵入字符,然后通過字符的改變,獲取相應的網(wǎng)絡(luò)請求數(shù)據(jù),最后通過AnimatedVisibility顯示與隱藏搜索建議列表

富文本

通過逐字匹配輸入框內(nèi)的字符與搜索建議item內(nèi)容,然后輸入框的字符存在搜索建議列表中的文字就加入高亮顯示列表中,因為采用buildAnnotatedString,可以讓文本顯示多種不同風格,所以最后將字符內(nèi)容區(qū)別為高亮顏色和普通文本兩種文本,并讓其進行顯示

@Composable
fun RichText(selectColor: Color,unselectColor: Color,fontSize:TextUnit = TextUnit.Unspecified,searchValue: String,matchValue: String
){val richText = buildAnnotatedString {repeat(matchValue.length){val index = if (it < searchValue.length) matchValue.indexOf(searchValue[it]) else -1if (index == -1){withStyle(style = SpanStyle(fontSize = fontSize,color = unselectColor,)){append(matchValue[it])}}else{withStyle(style = SpanStyle(fontSize = fontSize,color = selectColor,)){append(matchValue[index])}}}}Text(text = richText,maxLines = 1,overflow = TextOverflow.Ellipsis,modifier = Modifier.fillMaxWidth(),)
}

搜索結(jié)果

搜索結(jié)果也是由ScrollableTabRow+HorizontalPager完成子頁面的滑動切換,但是與上述不同的是,所展現(xiàn)的Tab與內(nèi)容并不是固定,而是根據(jù)后端返回的數(shù)據(jù)進行自動生成的。由于其他子頁面的內(nèi)容都是由LazyColumn進行展現(xiàn),而綜合界面有需要將其他界面的數(shù)據(jù)進行集中,所以就必須LazyColumn嵌套LazyColumn,然后這在Compose中是不被允許的,所以就將子Page的LazyColumn,使用modifier.heightIn(max = screenHeight.dp)進行高度限制,高度可以取屏幕高度,并且多個item之間都是取屏幕高度,之間不會存在間隙

視頻詳情

視頻播放功能暫未實現(xiàn)完成,因為獲取的API返回的URL進行播放一直為403,被告知權(quán)限不足,在網(wǎng)上進行多番查詢未果,所以暫且擱置。視頻庫采用的Google的ExoPlayer

合集

每個視頻返回的內(nèi)容數(shù)據(jù)格式一致,但具體內(nèi)容不一致,有的視頻存在排行信息、合集等,就通過AnimatedVisibility進行顯示和隱藏,將所有結(jié)果進行列出,然后在ViewModel通過解析數(shù)據(jù),并改變相應的狀態(tài)值,view即可進行重組

信息

Coroutines進行網(wǎng)絡(luò)請求管理,避免回調(diào)地獄

在日常開發(fā)中網(wǎng)絡(luò)請求必不可少,在傳統(tǒng)View+java開發(fā)中使用Retrifit或者okhttp進行網(wǎng)絡(luò)請求最為常見,但大多數(shù)場景中,后一個API需要前一個API數(shù)據(jù)內(nèi)字段值,此時就需要callback進行操作,回調(diào)一次獲取代碼依舊看起來簡潔,可讀,但次數(shù)一旦增多,則會掉入回調(diào)地獄。Google后續(xù)推出的協(xié)程完美解決此類問題,協(xié)程的主要核心就是“通過非阻塞的代碼實現(xiàn)阻塞功能”,具體代碼如下

添加suspend

以下為示例代碼,通過給接口添加suspend標志符,告知外界次方法需要掛起

@GET("xxxxx")suspend fun getVideoDetail(@Query("aid")aid:Int):BaseResponse<VideoDetail>

withContext

getVideoDetail掛起函數(shù)返回一個字段值,然后通過withContext包裹,使其進行阻塞,然后將返回值進行返回,后續(xù)的getVideoUrl掛起函數(shù)就可以使用前一個接口返回的數(shù)據(jù);需要注意的是,函數(shù)都需為suspend修飾的方法,并且在統(tǒng)一協(xié)程域中,否則會出現(xiàn)異常

 viewModelScope.launch(Dispatchers.Main) {try {withContext(Dispatchers.Main){val cid = withContext(Dispatchers.IO){getVideoDetail(_videoState.value.aid)}val url = withContext(Dispatchers.IO){getVideoUrl(avid = _videoState.value.aid, cid = cid)}if (url.isNotEmpty()){play(url)}getRelatedVideos(_videoState.value.aid)}}catch (e:Exception){Log.d("VDetailViewModel",e.message.toString())}}

Git項目鏈接

Git項目鏈接

此Demo并未完全完善,尤其是播放界面,由于采用Bilibili API獲取的視頻URL,在播放時一直返回403錯誤,被告知沒有權(quán)限,在根據(jù)文檔進行使用以及網(wǎng)上查詢未果之后,只能暫且擱置此功能。

http://www.risenshineclean.com/news/31361.html

相關(guān)文章:

  • 烏魯木齊可以做網(wǎng)站的公司有哪些重慶網(wǎng)站seo教程
  • 網(wǎng)站開發(fā)語言是什么app開發(fā)多少錢
  • 西安企業(yè)網(wǎng)站建設(shè)哪家好怎么用手機創(chuàng)建網(wǎng)站
  • 什么網(wǎng)站ppt做的好免費的seo優(yōu)化工具
  • 日語網(wǎng)站設(shè)計怎么做百度推廣平臺
  • 資陽視頻網(wǎng)站建設(shè)廣州seo服務公司
  • 房地產(chǎn)網(wǎng)站解決方案女孩子做運營是不是壓力很大
  • 企業(yè)網(wǎng)站 asp php網(wǎng)絡(luò)優(yōu)化工具app手機版
  • 哪些專門做批發(fā)的網(wǎng)站有哪些短網(wǎng)址鏈接生成
  • 網(wǎng)站制作文案杭州長治seo顧問
  • seo發(fā)布網(wǎng)站某網(wǎng)站搜索引擎優(yōu)化
  • 網(wǎng)站開發(fā)包含上線嗎網(wǎng)絡(luò)營銷的六大功能
  • wordpress網(wǎng)站例昆明網(wǎng)絡(luò)營銷
  • 在國外做盜版電影網(wǎng)站嗎seo發(fā)帖工具
  • 做內(nèi)貿(mào)的網(wǎng)站武漢網(wǎng)站設(shè)計十年樂云seo
  • 做網(wǎng)站需要備案么長沙seo培訓班
  • 撫順市城市建設(shè)檔案館網(wǎng)站安卓優(yōu)化大師新版
  • 做平臺網(wǎng)站外包多少錢啊常見的網(wǎng)絡(luò)營銷方式有哪幾種
  • 京東網(wǎng)上購物平臺如何快速優(yōu)化網(wǎng)站排名
  • 網(wǎng)站制作方案書博客網(wǎng)
  • 訂閱號欄目里做微網(wǎng)站網(wǎng)站排名靠前
  • 網(wǎng)絡(luò)營銷專業(yè)建議百度seo優(yōu)化收費標準
  • 兩學一做教育考試網(wǎng)站微信小程序怎么開通
  • WordPress微信密碼seo初級入門教程
  • 物聯(lián)網(wǎng)型網(wǎng)站開發(fā)企業(yè)網(wǎng)站建設(shè)服務
  • 做網(wǎng)站買域名網(wǎng)絡(luò)營銷策劃方案3000字
  • 建筑室內(nèi)設(shè)計公司排名seo人工智能
  • 中國建設(shè)門戶網(wǎng)站紀念幣看seo
  • 做網(wǎng)站一般收取多少錢國際重大新聞
  • 深圳市城鄉(xiāng)和建設(shè)局網(wǎng)站首頁貼吧推廣