Android-自定義View的實戰學習總結

一、自定義View歌詞界面

LrcView?類-->自定義的歌詞視圖

1. 構造函數和屬性初始化

????????自定義 View 通常需要提供多個構造函數以支持不同的初始化方式。在?LrcView?中,提供了四個構造函數,最終調用?super?父類構造函數完成初始化,?context.obtainStyledAttributes?方法獲取自定義屬性。

constructor(context: Context) : this(context, null)
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
constructor(context: Context,attrs: AttributeSet?,defStyleAttr: Int
) : this(context, attrs, defStyleAttr, 0)
constructor(context: Context,attrs: AttributeSet?,defStyleAttr: Int,defStyleRes: Int
) : super(context, attrs, defStyleAttr, defStyleRes) {val ta = context.obtainStyledAttributes(attrs, R.styleable.AbstractLrcView)// 獲取自定義屬性mNormalTextSize = ta.getDimension(R.styleable.AbstractLrcView_lrcTextSize,mResources.getDimension(R.dimen.lrc_text_size))// ... 其他屬性獲取ta.recycle()// 初始化畫筆等mNormalPaint.isAntiAlias = truemNormalPaint.textSize = mNormalTextSize// ... 其他畫筆初始化
}
2. 測量和布局

onMeasure?方法:雖然代碼中未給出?onMeasure?方法,但在自定義 View 中,通常需要重寫該方法來測量 View 的大小,以確定其寬度和高度。

onLayout?方法:重寫?onLayout?方法來確定子 View 的位置和大小,或者進行一些初始化操作。在?LrcView?中,當布局發生變化時,會重新設置播放按鈕和時間線的邊界,并初始化歌詞列表。

override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {super.onLayout(changed, left, top, right, bottom)if (changed) {val width = mPlayDrawable.intrinsicWidthval height = mPlayDrawable.intrinsicHeightval l = (mTimeTextWidth - height) / 2val t = getHeight() / 2 - width / 2val r = l + widthval b = t + widthmPlayDrawable.setBounds(l, t, r, b)mTimeLineDrawable.setBounds(mTimeTextWidth,getHeight() / 2 - mTimeLineDrawable.intrinsicHeight / 2,getWidth() - mTimeTextWidth,getHeight() / 2 + mTimeLineDrawable.intrinsicHeight / 2)initEntryList()}
}
?3.繪制

????????重寫?onDraw?方法來繪制 View 的內容。在?LrcView?中,根據不同的歌詞狀態(顯示歌詞、加載歌詞、無歌詞等)繪制不同的內容,包括播放按鈕、時間線、歌詞文本等。

@Synchronized
override fun onDraw(canvas: Canvas) {val centerY = height / 2when (mLrcStatus) {STATUS_SHOW_LRC -> {val centerLine = getCenterLine()if (mShowTimeline && !isSimpleMode) {// 繪制播放按鈕mPlayDrawable.let {it.setBounds(mDrawableMargin,centerY - mPlayDrawable.intrinsicHeight / 2,mDrawableMargin + mPlayDrawable.intrinsicWidth,centerY + mPlayDrawable.intrinsicHeight / 2)it.draw(canvas)}// 繪制時間線mTimePaint.color = mTimelineColorcanvas.drawLine(mTimeTextWidth.toFloat(),centerY.toFloat(),(getWidth() - mTimeTextWidth ).toFloat(),centerY.toFloat(),mTimePaint)mTimeLineDrawable.draw(canvas)// ... 其他繪制操作}// ... 繪制歌詞文本}STATUS_LOADING_LRC -> {mNormalPaint.color = mCurrentTextColordrawText(canvas, mResources.getString(R.string.loading_lrc), centerY.toFloat())}STATUS_EMPTY_LRC -> {mNormalPaint.color = mCurrentTextColordrawText(canvas, mResources.getString(R.string.no_lrc), centerY.toFloat())}else -> {mNormalPaint.color = mCurrentTextColordrawText(canvas, mResources.getString(R.string.no_lrc), centerY.toFloat())}}super.onDraw(canvas)
}
4.事件處理

使用?GestureDetector?來處理觸摸事件,如滑動、點擊等。在?LrcView?中,通過?GestureDetector.SimpleOnGestureListener?監聽不同的手勢事件,并根據事件類型進行相應的處理。

private val mSimpleOnGestureListener: GestureDetector.SimpleOnGestureListener =object : GestureDetector.SimpleOnGestureListener() {override fun onDown(event: MotionEvent): Boolean {if (hasLrc() && onPlayClickListener != null) {mScroller.forceFinished(true)mTouching = truemSlideing = falseinvalidate()}return true}override fun onScroll(e1: MotionEvent?,e2: MotionEvent,distanceX: Float,distanceY: Float): Boolean {synchronized(this@LrcView) {if (hasLrc() && !isSimpleMode) {// 添加播放按鈕點擊事件才能顯示時間線if (onPlayClickListener != null) {removeCallbacks(hideTimelineRunnable)mShowTimeline = true}if (!mSlideing) {mSlideing = truemSlideListener?.onSlideStart()}mOffset += -distanceYmOffset = Math.min(mOffset, getOffset(0))mOffset = Math.max(mOffset, getOffset(mLrcEntryList.size - 1))invalidate()return true}}return super.onScroll(e1, e2, distanceX, distanceY)}// ... 其他手勢事件處理}// 在構造函數中初始化 GestureDetector
mGestureDetector = GestureDetector(context, mSimpleOnGestureListener)
mGestureDetector.setIsLongpressEnabled(false)override fun onTouchEvent(event: MotionEvent): Boolean {return mGestureDetector.onTouchEvent(event) || super.onTouchEvent(event)
}
5.滾動處理

????????使用?Scroller?來實現平滑滾動效果。在?onFling?事件中,調用?mScroller.fling?方法啟動滾動,并在?computeScroll?方法中更新滾動位置。雖然代碼中未給出?computeScroll?方法,但通常的實現如下????????:

override fun computeScroll() {if (mScroller.computeScrollOffset()) {mOffset = mScroller.currY.toFloat()invalidate()}
}

SingleLineLrcView類---單行歌詞類

?測量方法?onMeasure
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {var widthMeasureSpec = widthMeasureSpecvar heightMeasureSpec = heightMeasureSpecval widthMode = MeasureSpec.getMode(widthMeasureSpec)if (mMaxWidth > 0) {if (widthMode == MeasureSpec.EXACTLY) {widthMeasureSpec = MeasureSpec.makeMeasureSpec(Math.min(mMaxWidth.toInt(), MeasureSpec.getSize(widthMeasureSpec)), MeasureSpec.EXACTLY)} else {widthMeasureSpec = MeasureSpec.makeMeasureSpec(mMaxWidth.toInt(), MeasureSpec.EXACTLY)}}val heightMode = MeasureSpec.getMode(heightMeasureSpec)if (heightMode == MeasureSpec.EXACTLY) {heightMeasureSpec = MeasureSpec.makeMeasureSpec(Math.max(Math.max(mNormalTextSize, mCurrentTextSize).toInt(), MeasureSpec.getSize(heightMeasureSpec)), MeasureSpec.EXACTLY)} else {heightMeasureSpec = MeasureSpec.makeMeasureSpec(Math.max(mNormalTextSize, mCurrentTextSize).toInt(), MeasureSpec.EXACTLY)}setMeasuredDimension(getDefaultSize(suggestedMinimumWidth, widthMeasureSpec),getDefaultSize(suggestedMinimumHeight, heightMeasureSpec))
}
  • 根據?mMaxWidth?和測量模式調整寬度測量規格。
  • 根據文本大小和測量模式調整高度測量規格。
  • 最后調用?setMeasuredDimension?方法設置測量后的寬高
?繪制方法?onDraw
override fun onDraw(canvas: Canvas) {val centerY = (height / 2).toFloat()when (mLrcStatus) {STATUS_SHOW_LRC -> {if (mLastLrcTime >= 0) {updateShowLine()if (mLrcIndex >= 0 && mLrcIndex < mLrcEntryList.size) {val text: Stringif (mLrcIndex > 0 && mLrcEntryList[mLrcIndex - 1].time == mLrcEntryList[mLrcIndex].time) {text = mLrcEntryList[mLrcIndex - 1].text} else {text = mLrcEntryList[mLrcIndex].text}if (!TextUtils.isEmpty(text)) {drawFocusText(canvas, text, mCurrentPercent, centerY)}}}}STATUS_LOADING_LRC -> {mNormalPaint.color = mCurrentTextColordrawText(canvas, mResources.getString(R.string.loading_lrc), centerY)}else -> {mNormalPaint.color = mCurrentTextColordrawText(canvas, mResources.getString(R.string.no_lrc), centerY)}}super.onDraw(canvas)
}
  • 根據?mLrcStatus?的不同狀態,繪制不同的內容。
  • 如果狀態為?STATUS_SHOW_LRC,則更新顯示的歌詞行,并調用?drawFocusText?方法繪制高亮的歌詞。
  • 如果狀態為?STATUS_LOADING_LRC,則繪制 “Loading lyrics” 的提示信息。
  • 其他狀態則繪制 “沒有歌詞” 的提示信息。
?自定義繪制方法
private fun drawFocusText(canvas: Canvas, text: String, percent: Float, y: Float) {// ...
}private fun drawText(canvas: Canvas, text: String, y: Float) {// ...
}
  • drawFocusText?方法用于繪制高亮的歌詞,通過?canvas.clipRect?方法實現歌詞的漸變效果。
  • drawText?方法用于繪制普通的文本。

歌詞自定義View總結:

????????LrcView?類繼承自?AbstractLrcView,實現了一個功能豐富的自定義歌詞視圖,綜合運用了多個自定義 View 知識點。

????????在構造函數和屬性初始化方面,提供了四個構造函數,最終調用父類構造函數完成初始化,并通過?context.obtainStyledAttributes?獲取自定義屬性,同時初始化畫筆等。

????????測量和布局上,雖未給出?onMeasure?方法,但重寫?onLayout?方法,在布局變化時重新設置播放按鈕和時間線邊界,并初始化歌詞列表。

????????繪制時重寫?onDraw?方法,依據不同歌詞狀態(顯示、加載、無歌詞等)繪制不同內容,如播放按鈕、時間線、歌詞文本等。事件處理使用?GestureDetector?監聽不同手勢事件并處理,像滑動、點擊等。滾動處理借助?Scroller?實現平滑滾動,在?onFling?啟動滾動,在?computeScroll?更新位置。還通過?R.styleable.AbstractLrcView?定義自定義屬性,在構造函數中獲取其值以配置外觀和行為。最后,在?onDraw?方法中根據?mLrcStatus?不同狀態繪制對應內容,實現狀態管理。


????????SingleLineLrcView?是一個繼承自?AbstractLrcView?的自定義 Android View,用于顯示單行歌詞。

????????它定義了正常和高亮文本的畫筆、顏色、大小等屬性,在構造函數中調用?init?方法進行初始化,通過?obtainStyledAttributes?獲取自定義屬性值并設置畫筆屬性和字體樣式。提供了?setNormalColor?和?setCurrentColor?方法來動態改變文本顏色并刷新視圖。

????????onMeasure?方法根據最大寬度和測量模式調整視圖的寬高。onDraw?方法根據歌詞狀態(顯示歌詞、加載歌詞、無歌詞)繪制不同內容,若顯示歌詞則更新顯示行并調用?drawFocusText?方法繪制高亮歌詞,該方法通過?canvas.clipRect?實現歌詞漸變效果;若加載歌詞或無歌詞則調用?drawText?方法繪制提示信息。此外,還提供了?getLrcWidth?方法用于計算歌詞顯示的寬度。?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/89802.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/89802.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/89802.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Maven 在 Eclipse 中的使用指南

Maven 在 Eclipse 中的使用指南 概述 Maven 是一個強大的構建自動化工具,用于項目管理和構建。它簡化了項目構建、依賴管理和項目報告等任務。Eclipse 是一個流行的集成開發環境(IDE),支持多種編程語言,包括 Java。本文將詳細介紹如何在 Eclipse 中使用 Maven 進行項目管…

zxing去白邊

2025年了&#xff0c;可能干不了幾年了&#xff0c;還能寫這種文章還是有點可笑。 背景 zxing庫生成的二維碼自帶白邊 分析 生產二維碼主要分兩步&#xff1a; 1.用QRCodeWriter生成BitMatrix信息 2.根據信息生成bitmap 問題在1。 生成二維碼的尺寸實際是有一些規格的&a…

Linux操作系統之文件(三):緩沖區

前言&#xff1a; 上節課我們講授重定向的概念時&#xff0c;曾提到了一點緩沖區的概念。本文將會為大家更詳細的帶來緩沖區的有關內容&#xff1a;用戶級緩沖區是什么&#xff0c;以及其與內核級緩沖區的關系&#xff0c;最后&#xff0c;我會為大家模擬實現一下stdio.h的關于…

Linux云計算基礎篇(7)

一、< 輸入重定向 wc -l < filelist .txt 統計數據&#xff0c;從file這個文件拿結果。 二、tr 轉換字符命令 $ tr A-Za-z<.bash_profile 將bash_profile文件中的大寫字符全部轉成小寫字符 三、管道符&#xff08;|&#xff09; com…

【學習筆記】Lean4基礎 ing

文章目錄 概述參考文檔運行程序elan 命令行工具lean 命令行工具lake 命令行工具運行單文件程序Hello, world!驗證 Lean4 證明 運行多文件項目 Lean4 基礎語法注釋表達式求值變量和定義定義類型變量 定義函數命名規則命名空間數據類型結構體構造子模式匹配多態List 列表Option 可…

FPGA實現40G網卡NIC,基于PCIE4C+40G/50G Ethernet subsystem架構,提供工程源碼和技術支持

目錄 1、前言工程概述免責聲明 3、相關方案推薦我已有的所有工程源碼總目錄----方便你快速找到自己喜歡的項目我這里已有的以太網方案 4、工程詳細設計方案工程設計原理框圖測試用電腦PClE4CDMA40G/50G Ethernet subsystem工程源碼架構驅動和測試文件 5、Vivado工程詳解1詳解&a…

SAP從入門到放棄系列之流程管理概述

文章目錄前言1.Process Management&#xff08;過程管理&#xff09;2.關鍵術語2.1Control recipe destination2.2 Process instruction characteristic2.3 Process message characteristic2.4 Process instruction category2.5 Process message category2.6 PI sheet3.關鍵配置…

RCLAMP0554S.TCT升特Semtech 5通道TVS二極管,0.5pF+20kV防護,超高速接口!

RCLAMP0554S.TCT&#xff08;Semtech&#xff09;產品解析與推廣文案 一、產品定位 RCLAMP0554S.TCT是Semtech&#xff08;升特半導體&#xff09;推出的5通道超低電容TVS二極管陣列&#xff0c;專為超高速數據接口&#xff08;USB4/雷電4/HDMI 2.1&#xff09;提供靜電放電&a…

【人工智能】DeepSeek的AI實驗室:解鎖大語言模型的未來

《Python OpenCV從菜鳥到高手》帶你進入圖像處理與計算機視覺的大門! 解鎖Python編程的無限可能:《奇妙的Python》帶你漫游代碼世界 DeepSeek作為中國AI領域的先鋒,以其開源大語言模型(LLM)DeepSeek-V3和DeepSeek-R1在全球AI研究中掀起波瀾。本文深入探討DeepSeek AI實驗…

nacos+nginx動態配置大文件上傳限制

前言 今天還要跟大家分享的一個點就是微服務網關gateway用webflux響應式不用servlet后&#xff0c;引發的一個忽略點差點在演示的時候炸鍋&#xff0c;也不多講廢話&#xff0c;說說現象&#xff0c;說說處理就了事。 一、上傳超過20MB的視頻報錯 配置在nacos里&#xff0c;讀…

mr 任務運行及jar

mainclass如下&#xff1a;LoggingDriver

Python 數據分析:numpy,抽提,整數數組索引與基本索引擴展(元組傳參)。聽故事學知識點怎么這么容易?

目錄1 代碼示例2 歡迎糾錯3 論文寫作/Python 學習智能體------以下關于 Markdown 編輯器新的改變功能快捷鍵合理的創建標題&#xff0c;有助于目錄的生成如何改變文本的樣式插入鏈接與圖片如何插入一段漂亮的代碼片生成一個適合你的列表創建一個表格設定內容居中、居左、居右Sm…

ECU開發工具鏈1.10版:更強大的測量、校準與數據分析體驗.

汽車電子開發與測試領域&#xff0c;高效、精準且安全的工具是成功的基石。DiagRA X 作為一款廣受認可的 Windows 平臺綜合解決方案&#xff0c;持續引領行業標準。其最新發布的 1.10 版本帶來了顯著的功能增強與用戶體驗優化&#xff0c;進一步鞏固了其在 ECU 測量、校準、刷寫…

Qt C++串口SerialPort通訊發送指令讀寫NFC M1卡

本示例使用的發卡器&#xff1a;https://item.taobao.com/item.htm?spma21dvs.23580594.0.0.52de2c1bVIuGpf&ftt&id18645495882 一、確定已安裝Qt Serial Port組件 二、在.pro項目文件聲明引用Serialport組件 三、在.h頭文件內引用Serialport組件 四、在.cpp程序中實…

Go 語言開發中用戶密碼加密存儲的最佳實踐

在現代 Web 應用開發中&#xff0c;用戶密碼的安全存儲是系統安全的重要環節。本文將結合 Go 語言和 GORM 框架&#xff0c;詳細介紹用戶密碼加密存儲的完整解決方案&#xff0c;包括數據庫模型設計、加密算法選擇、鹽值加密實現等關鍵技術點。 一、數據庫模型設計與 GORM 實踐…

優化Facebook廣告投放的五大關鍵策略

一、精確篩選目標國家用戶在Audience的locations設置目標國家時&#xff0c;務必勾選"People living in this location"選項。系統默認會選擇"People living in this location or recently in this location"&#xff0c;這會擴大受眾范圍&#xff0c;包含…

Debian-10-standard用`networking`服務的`/etc/network/interfaces`配置文件設置多網卡多IPv6

Debian-10-buster-standard用networking服務的/etc/network/interfaces配置文件設置多網卡多IPv6 Debian-10-buster-standard用networking服務的/etc/network/interfaces配置文件設置多網卡多IPv6 250703_123456 三塊網卡 : enp0s3 , enp0s8 , enp0s9 /etc/network/interfac…

對話式 AI workshop:Voice Agent 全球五城開發實錄

過去幾個月&#xff0c;TEN Framework 團隊與 Agora 和聲網圍繞 “對話式AI”題&#xff0c;踏上了橫跨全球五大城市的精彩旅程——東京、舊金山、巴黎、北京、京都。 五場精心籌備的Workshop 場場爆滿&#xff0c; 匯聚了來自當地及全球的開發者、創業者、產品經理與語音技術愛…

算法學習筆記:6.深度優先搜索算法——從原理到實戰,涵蓋 LeetCode 與考研 408 例題

在計算機科學領域&#xff0c;搜索算法是解決問題的重要工具&#xff0c;其中深度優先搜索&#xff08;Depth-First Search&#xff0c;簡稱 DFS&#xff09;憑借其簡潔高效的特性&#xff0c;在圖論、回溯、拓撲排序等眾多場景中發揮著關鍵作用。無論是 LeetCode 算法題&#…

vue create 和npm init 創建項目對比

以下是關于 vue create 和 npm init 的對比分析&#xff1a; 1. 定位與功能 vue create 定位&#xff1a;Vue 官方提供的腳手架工具&#xff0c;基于 Vue CLI&#xff0c;用于快速創建標準化的 Vue 項目&#xff0c;支持 Vue 2 和 Vue 3。功能&#xff1a;提供交互式配置&…