編輯:關於Android編程
//** 實現一種動畫效果 */
override func scrollViewDidScroll(scrollView: UIScrollView) {
//獲取指定的cell
guard let indexRows = tableView.indexPathsForVisibleRows else {
return
}
//** 獲取中間的cell */
let firstCell = indexRows.first?.row ?? 0
let lastCell = indexRows.last?.row ?? 0
//** 計算中間的cell */
let middleCell = Int(Float(firstCell + lastCell) * 0.5)
//** 遍歷指定的所有的cell */
for indexRow in indexRows {
//取出cell
let cell = tableView.cellForRowAtIndexPath(indexRow)
cell?.x = CGFloat(abs(indexRow.row - middleCell) * 40)
print(indexRow.row - middleCell)
}
}
//** 歌曲名稱 */
@IBOutlet weak var nameLabel: UILabel!
//** 歌手名稱 */
@IBOutlet weak var singerLabel: UILabel!
//** 總音樂時間 */
@IBOutlet weak var totalLabel: UILabel!
//** 歌手背景圖片 */
@IBOutlet weak var singerImageView: UIImageView!
//** 能讓現實歌詞的view拖動的scrollView */
@IBOutlet weak var lrcSImageView: UIScrollView!
//** 歌手專輯圖片 */
@IBOutlet weak var backImageView: UIImageView!
//** 暫停或者播放 */
@IBOutlet weak var playOrPause: UIButton!
//** 當前時間 */
@IBOutlet weak var currentLabel: UILabel!
//** 顯示歌詞的label */
@IBOutlet weak var lrcLabel: UILabel!
//** 滑塊 */
@IBOutlet weak var silderProgress: UISlider!
//** 繼續播放音樂 */
func playMusic() {
player?.play()
}
//** 暫停播放 */
func pauseMusic() {
player?.pause()
}
//** 創建音樂播放列表 (XFJQQMusicListItem: 音樂列表的模型)*/
var musicList : [XFJQQMusicListItem]?
//** 提供一個記錄歌曲的索引 */
var index = 0 {
didSet {
//判斷
if index < 0 {
index = (musicList?.count ?? 0) - 1
}
if index > ((musicList?.count ?? 0) - 1){
index = 0
}
}
}
class XFJQQMusicNewMessageModel: NSObject {
//* 讓該模型擁有顯示歌單的模型 /
var QQMusicItem : XFJQQMusicListItem?
//* 當前播放的時間 /
var costTime : NSTimeInterval = 0
//* 總播放的時間 /
var totalTime : NSTimeInterval = 0
//** 提供當前時間和總時間的get方法 */
var costFormatTime : String {
get{
return XFJQQTimeTool.getTime(costTime)
}
}
//** 提供總時間的get方法 */
var totalFormatTime : String {
get{
return XFJQQTimeTool.getTime(totalTime)
}
}
//** 播放的狀態 */
var isPlaying : Bool = false
}
//** 設置一個方法用來提供最新的數據 */
func getQQMusicNewMessage() ->XFJQQMusicNewMessageModel {
//** 給暫時歌詞中的模型屬性賦值,保持最新的數據 */
QQMusicMessageModel.QQMusicItem = musicList![index]
//** 當前播放的進度 */
QQMusicMessageModel.costTime = tool.player?.currentTime ?? 0
//** 歌曲的總時長 */
QQMusicMessageModel.totalTime = tool.player?.duration ?? 0
//** 播放狀態 */
QQMusicMessageModel.isPlaying = tool.player?.playing ?? false
//** 返回最新的數據模型 */
return QQMusicMessageModel
}
//設置一個方法對音樂的處理
func playMusic(musicM : XFJQQMusicListItem) {
//取出模型中的音樂文件名
let fileName = musicM.filename ?? ""
//傳入音樂文件名播放音樂
tool.playMusic(fileName)
//** 判斷如果列表中沒有音樂就直接返回 */
if musicList == nil {
return
}
//** 並且記錄當前的音樂索引 */
index = (musicList?.indexOf(musicM))!
}
//** 重新播放 */
func playCurrentMusic() ->() {
tool.playMusic()
}
//** 暫停播放 */
func pauseCurrentMusic() {
tool.pauseMusic()
}
//** 上一首 */
func preMusic() {
//索引遞減
index -= 1
//判斷
if let muiscM = musicList {
//取出模型
let music = muiscM[index]
//播放音樂
playMusic(music)
}
}
//** 下一首 */
func nextMusic() {
//索引增加
index += 1
//判斷
if let musicM = musicList {
//取出模型
let music = musicM[index]
//播放
playMusic(music)
}
}
class XFJQQTimeTool: NSObject {
class func getTime(time : NSTimeInterval) ->String {
let min = Int(time / 60)
let sec = Int(time) % 60
//** 轉成字符串 */
let resultStr = String(format: "%02d:%02d", min,sec)
//** 返回結果 */
return resultStr
}
}
//** 設置一次 */
private func setUpOnce() {
setUpViewOnce()
setUpSilder()
}
//** 設置多次 */
private func setUpTimes() {
setUpViewFrame()
setUpForeImage()
}
//** 只需要設置一次的數據 */
private func setUpDataTime() ->() {
let musicMessageModel = XFJQQMusicOperationTool.shareInstance.getQQMusicNewMessage()
//** 歌曲名稱 */
nameLabel.text = musicMessageModel.QQMusicItem?.name
//** 歌手背景圖片 */
singerImageView.image = UIImage(named: (musicMessageModel.QQMusicItem?.icon ?? ""))
//** 歌手專輯圖片 */
backImageView.image = UIImage(named: (musicMessageModel.QQMusicItem?.icon)!)
//** 歌手名稱 */
singerLabel.text = musicMessageModel.QQMusicItem?.singer
//** 歌曲總時間 */
totalLabel.text = "\(musicMessageModel.totalFormatTime)"
}
//** 設置多次的數據 */
func setUpDatasTimes() {
let musicMessageModel = XFJQQMusicOperationTool.shareInstance.getQQMusicNewMessage()
//** 當前播放時間 */
currentLabel.text = "\(musicMessageModel.costFormatTime)"
//** 進度條 */
silderProgress.value = Float(musicMessageModel.costTime / musicMessageModel.totalTime)
//** 播放狀態 */
playOrPause.selected = musicMessageModel.isPlaying
}
//** 播放和暫停 */
@IBAction func playAndPause(button : UIButton)
{
//判斷
button.selected = !button.selected
if button.selected {
XFJQQMusicOperationTool.shareInstance.playCurrentMusic()
}else{
XFJQQMusicOperationTool.shareInstance.pauseCurrentMusic()
}
}
//** 上一首 */
@IBAction func preButton()
{
let operationTool = XFJQQMusicOperationTool.shareInstance
//調用播放上一首的方法
operationTool.preMusic()
setUpDataTime()
}
//** 下一首 */
@IBAction func nextButton()
{
let operationTool = XFJQQMusicOperationTool.shareInstance
operationTool.nextMusic()
setUpDataTime()
}
@IBAction func popVC()
{
navigationController?.popViewControllerAnimated(true)
}
//** 設置定時器 */
func addTimer() {
//** 設置定時器 */
timer = NSTimer(timeInterval: 1, target: self, selector: "setUpDatasTimes", userInfo: nil, repeats: true)
//** 將定時器添加到主運行循環中 */
NSRunLoop.currentRunLoop().addTimer(timer!, forMode: NSRunLoopCommonModes)
}
//** 移除定時器 */
func removeTimer() ->() {
timer?.invalidate()
timer = nil
}
extension CALayer {
// 暫停動畫
func pauseAnimate()
{
let pausedTime: CFTimeInterval = convertTime(CACurrentMediaTime(), fromLayer: nil)
speed = 0.0;
timeOffset = pausedTime;
}
// 恢復動畫
func resumeAnimate()
{
let pausedTime: CFTimeInterval = timeOffset
speed = 1.0;
timeOffset = 0.0;
beginTime = 0.0;
let timeSincePause: CFTimeInterval = convertTime(CACurrentMediaTime(), fromLayer: nil) - pausedTime
beginTime = timeSincePause;
}
}
//恢復動畫
private func resumeAnimation() {
foreImageView.layer.resumeAnimate()
}
//暫停動畫
private func pauseAnimation() {
foreImageView.layer.pauseAnimate()
}
private func addAnimation() {
//移除上移個動畫
foreImageView.layer.removeAnimationForKey("rotation")
//添加動畫
let animation = CABasicAnimation(keyPath: "transform.rotation.z")
//設置相關屬性
animation.duration = 30
animation.fromValue = 0
animation.toValue = M_PI * 2
animation.repeatCount = MAXFLOAT
//播放完成後不需要移除動畫
animation.removedOnCompletion = false
//添加動畫
foreImageView.layer.addAnimation(animation, forKey: "rotation")
}
//** 播放和暫停 */
@IBAction func playAndPause(button : UIButton)
{
button.selected = !button.selected
//判斷
if button.selected {
XFJQQMusicOperationTool.shareInstance.pauseCurrentMusic()
pauseAnimation()
}else {
XFJQQMusicOperationTool.shareInstance.playCurrentMusic()
resumeAnimation()
}
}
//將歌詞的數據源交給展示歌曲的控制器來展示
lrcTVC.dataSource = lrcMs
//切換歌曲開始動畫
addAnimation()
//判斷如果是選擇暫停和開始的屬性做相應的動畫
if musicMessageModel.isPlaying {
resumeAnimation()
}else {
pauseAnimation()
}
class XFJQQLrcModel: NSObject {
//** 歌詞演唱的開始時間 */
var beginTime : NSTimeInterval = 0
//** 歌詞演唱的結束時間 */
var endTime : NSTimeInterval = 0
//** 歌詞的內容 */
var lrcStr : String = ""
}
//提供一個方法對某一行歌詞的返回(需要傳入當前播放的時間進度和模型中的開始時間,結束時間)
//采用元組的方式來達到讀取歌詞的經度
class func getLrcMusicRow(current : NSTimeInterval, lrcModel : [XFJQQLrcModel]) ->(row :Int, lrcM : XFJQQLrcModel){
//取出模型的個數
let count = lrcModel.count
//遍歷
for i in 0.. current {
return (i, lrcM)
}
}
return (0, XFJQQLrcModel())
}
[ti:]
[ar:]
[al:]
//設計一個方法用來處理歌詞
class func getLrcMusicData(fileName : String?) ->[XFJQQLrcModel] {
//獲取歌詞文件的路徑
guard let path = NSBundle.mainBundle().pathForResource(fileName, ofType: nil) else{
return [XFJQQLrcModel]()
}
//加載文件的內容
var contentLrc = ""
do {
contentLrc = try String(contentsOfFile: path)
}catch {
print(error)
return [XFJQQLrcModel]()
}
//將歌詞轉成一行一行組成的數組
let lrcArray = contentLrc.componentsSeparatedByString("\n")
//創建一個裝XFJQQLrcModel這種類型的數組
var lrcMs = [XFJQQLrcModel]()
//遍歷歌詞的組成的數組,然後放入lrcMs數組中
for lrcmStr in lrcArray {
//過濾掉垃圾數據
if lrcmStr.containsString("[ti:") || lrcmStr.containsString("[ar:") || lrcmStr.containsString("[al:") {
continue
}
//裝入上面的數組中
let lrcm = XFJQQLrcModel()
lrcMs.append(lrcm)
[00:00.89]傳奇
[00:02.34]作詞:劉兵
[00:03.82]作曲:李健
[00:05.48]演唱:王菲
//拿到的數據才是真正能解析的數據
//替換
let resultStr = lrcmStr.stringByReplacingOccurrencesOfString("[", withString: "")
//開始解析
let timeAndContent = resultStr.componentsSeparatedByString("]")
if timeAndContent.count == 2 {
let time = timeAndContent[0]
lrcm.beginTime = XFJQQTimeTool.getLrcFormatTime(time)
let content = timeAndContent[1]
lrcm.lrcStr = content
}
}
//遍歷所有的數組中的時間
let count = lrcMs.count
for i in 0..
—-> 6.2 對能處理的歌詞方法的調用
//** 該方法是對歌詞的開唱時間和結束時間的處理(根據傳入的字符串,返回一個確切的時間) */
class func getLrcFormatTime(time : String) ->NSTimeInterval {
//根據":"取出時間
let minAndSec = time.componentsSeparatedByString(":")
//判斷
if minAndSec.count == 2 {
//分鐘
let min = NSTimeInterval(minAndSec[0]) ?? 0
//秒鐘
let sec = NSTimeInterval(minAndSec[1]) ?? 0
//返回處理好的時間
return min * 60 + sec
}
//如果走到這裡,就直接返回0
return 0
}
7 將處理好的歌詞交給顯示歌詞的tableView來顯示
//獲取歌詞數據源
let lrcMs = XFJQQMusicLrcDataTool.getLrcMusicData(musicMessageModel.musicModel?.lrcname)
//打印出歌詞
print(lrcMs)
//將歌詞的數據源交給展示歌曲的控制器來展示
lrcTVC.dataSource = lrcMs
8 之間用來顯示歌詞的是UIView,現在我們使用UITableView來顯示,將使用到UIView顯示歌詞的地方全都用tableView來代替.
9 實現tableView中數據源發方法
///MARK : - 數據源方法
extension XFJQQLrcTVC {
//組
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
//每組的cell
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
//由模型數據決定
return dataSource.count
}
//每行cell的內容
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
//用該方法得到cell
let cell = XFJQQMusicLrcCell.getMusicLrcCell(tableView)
//判斷如果當前處在最中間的是正在播放的行數,那麼久賦值,否則就賦值為0
if indexPath.row == scrollRow {
cell.progress = progress
}else {
cell.progress = 0.0
}
//取出模型
let lrcStr = dataSource[indexPath.row]
//賦值
cell.lrcStr = lrcStr.lrcStr
return cell
}
}
10 自定義cell和自定義顯示歌詞的label
—-> 10.1 自定義cell(該部分是對cell中label的顯示)
@IBOutlet weak var lrcLabel: XFJQQMusicLrcLabel!
//提供一個屬性給展示歌詞的view中的歌詞狀態
var progress : Double = 0.0 {
didSet {
lrcLabel.progress = progress
}
}
var lrcStr : String = "" {
didSet {
lrcLabel.text = lrcStr
}
}
}
—-> 10.2 返回創建好的cell
///MARK : - 再累擴展中提供一個方法,返回cell
extension XFJQQMusicLrcCell {
class func getMusicLrcCell(tableView : UITableView) ->XFJQQMusicLrcCell {
let lrcCellID = "lrcCell"
var cell = tableView.dequeueReusableCellWithIdentifier(lrcCellID) as? XFJQQMusicLrcCell
if cell == nil {
cell = NSBundle.mainBundle().loadNibNamed("XFJQQMusicLrcCell", owner: nil, options: nil).first as? XFJQQMusicLrcCell
}
return cell!
}
}
—-> 10.3 自定義label
class XFJQQMusicLrcLabel: UILabel {
//自定義更新歌詞的進度(提供set方法)
var progress : Double = 0.0 {
didSet {
//重繪
setNeedsDisplay()
}
}
override func drawRect(rect: CGRect) {
super.drawRect(rect)
//設置填充顏色
UIColor.greenColor().set()
let progressloat = CGFloat(progress)
let rect = CGRectMake(rect.origin.x, rect.origin.y, rect.size.width * progressloat, rect.size.height)
UIRectFillUsingBlendMode(rect, CGBlendMode.SourceIn)
}
}
11 當有歌詞歌曲變化的時候,立刻更新歌詞的數據
var dataSource : [XFJQQLrcModel] = [XFJQQLrcModel]() {
didSet {
tableView.reloadData()
}
}
12 在顯示歌詞的tableView控制器中設置一個屬性用來提供正在歌唱的歌詞始終處於tableView的中間cell
//設置歌詞滾動到中間(set方法)
var scrollRow : Int = 0 {
didSet {
//判斷防止重復滾動(用舊值作為判斷)
if scrollRow != oldValue {
tableView.reloadRowsAtIndexPaths(tableView.indexPathsForVisibleRows!, withRowAnimation: UITableViewRowAnimation.Fade)
let indexPath = NSIndexPath(forRow: scrollRow, inSection: 0)
//拿到角標滾動到哪個位置
tableView.scrollToRowAtIndexPath(indexPath, atScrollPosition: UITableViewScrollPosition.Middle, animated: true)
//刷新(如果先做動畫再刷新的話,動畫還沒做完就直接刷新了,會出現顯示歌詞的view跳動,所以需要先刷新再動畫)
// tableView.reloadRowsAtIndexPaths(tableView.indexPathsForVisibleRows!, withRowAnimation: UITableViewRowAnimation.Fade)
}
}
}
13 給正在歌唱的cell中的歌詞設置進度(set方法)
var progress : Double = 0.0 {
didSet {
//獲取當前正在行號
let indexPath = NSIndexPath(forRow: scrollRow, inSection: 0)
//將當前的cell類型轉換
let cell = tableView.cellForRowAtIndexPath(indexPath) as? XFJQQMusicLrcCell
//給cell中progress賦值
cell?.progress = progress
}
}
14 處理已進入展示歌詞的頁面,歌詞的頭部位置處於tableView的中間部位
//在view將要布局的時候對顯示歌詞的view布局,讓其頭部顯示在中間
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
tableView.contentInset = UIEdgeInsets(top: tableView.height * 0.5, left: 0, bottom: tableView.height * 0.5, right: 0)
}
15 更新滾動時候的歌詞(該方法調用頻繁)
//更新滾動的時候歌詞(調用頻率頻繁)
func updateLrc () ->() {
//獲取歌曲的最新值
let musicMessageModel = XFJQQMusicOperationTool.shareInstance.getUpNewMusicMessage()
//獲取最新歌詞的行號
let rowLrm = XFJQQMusicLrcDataTool.getLrcMusicRow(musicMessageModel.costTime, lrcModel:lrcTVC.dataSource)
let row = rowLrm.row
//賦值
lrcTVC.scrollRow = row
//給歌詞標簽展示歌詞
let lrcm = rowLrm.lrcM
lrcLabel.text = lrcm.lrcStr
//歌詞進度(當前的進度除以在總進度)
let value = (musicMessageModel.costTime - lrcm.beginTime) / (lrcm.endTime - lrcm.beginTime)
//賦值
lrcLabel.progress = value
//給展示歌詞的view賦值
lrcTVC.progress = value
16 怎麼才能讓歌詞滾動呢? 通過系統的一個類CADisplayLink(和定時器作用差不多,這裡我代碼就不寫上了)
七 注意點和性能優化
1 注意 : 退出後台動畫不再旋轉—> 解決方案 : 動畫完成之後,不需要移除
2 怎麼樣將歌詞分割成一行一行,然後展示呢?—>解決方案 : 通過設置一個歌詞模型來展示
3 歌詞不滾動?—> 解決方案 : CADisplayLink(類似定時器)
4 歌詞顯示的時候出現了刷新問題?—> 解決方案 : 先刷新,在滾動
5 性能優化 : 圖片繪制的次數太多?
—-> 5.1 解決方案 : 通過設置一個屬性記錄歌詞的行值,然後判斷是否是同一行
6 當前顯示的時間一直在更新,暫停的時候不需要更新,也可以優化(留給你們實現了)
八 功能補充(額外的想法,還未實現)
1 拖動進度條達到快進的目的
2 後台模式(後面會抽時間補上)
3 搖一搖切換歌曲
九 總結
1 該篇博客寫的思路還算清晰,唯一的缺點是我無法在後面歌詞顯示部分更加詳細點說明,因為該處真是沒辦法說明了,只能奉上我寫的代碼了.
2 本篇博客是基於QQ音樂的swift版本,還有很多的功能沒有實現,希望後面我能抽出時間進一步的完善也希望閱讀過我博客的程序猿能提供意見.
3 最後,由於時間一直沒有抽出來,這使得博客更新的速度有點慢,後續我會慢慢補上.如果大家覺的我寫的還可以,麻煩大家關注我的官方博客,謝謝!!!!
intent主要包括隱式意圖和顯式意圖。顯式意圖通常主要是啟動本應用中的Activity之間的數據,而隱式意圖則常見於啟動系統中的某些特定的動作,比如打電話,發短信,或者
針對Android Studio的系列文章,都是一個小問題為一篇,並沒有整理到一起,主要是方便大家根據自己的需要來查找,同時為了便於大家理解,都會直接上圖。 我這裡使用的
簡單實現的短信發送器,效果截圖如下: 其中的java代碼如下:package com.mxy.smssend; import java.util.ArrayList;
前言因為工作需要可能要用到JNI開發,本篇文章就分享一下我在這方面的實踐,以前我們使用Eclipse做NDK開發,非常麻煩,需要配cygwin的編譯環境,後面NDK功能完