Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android仿天天動聽歌曲自動滾動view

Android仿天天動聽歌曲自動滾動view

編輯:關於Android編程

最近項目中要做一個類似天天動聽歌曲自動滾動行數的效果。首先自己想了下Android要滾動的那就是scroller類或者scrollto、scrollby結合了,或者view.layout()方法,或者使用動畫。但是要循環滾動,貌似這些到最後一行滾動到第一行都有往回滾的效果,都不是很好的解決方法。怎麼會忘記了可以繪制事件萬物的的canvas呢。好吧,既然找到了,那就用這個方案吧!但是天天動聽歌曲還有一個手動滑動的效果,貌似這篇文章沒寫。既然這樣,那就自己來寫下吧!實現之前還是先看下天天動聽的效果:

正文

想法1:獲取滑動的距離,然後計算滑動了多少行,然後更新數據。實現起來貌似效果不咋地。
想法2:我們可以看的出來他滾動是一行一行的滾動的,只是根據滾動的快慢來決定滾動行數的快慢。既然這樣的話,只要滾動了,就一定時間的去一行行的滾動,然後根據滾動的速度來決定更新的間隔時間。

嗯,想好了怎麼實現,現在就來寫代碼吧。

先來寫一個類,繼承TextView

VerticalScrollTextView.class

public class VerticalScrollTextView extends TextView implements Runnable{
 //繪制歌詞畫筆
 private Paint mContentPaint;
 //繪制基線畫筆
 private Paint mLinePaint;
 //繪制滑動進度背景畫筆
 private Paint mRectPaint;
 //歌詞數據
 private List<Sentence> mDataList;
 //行數
 private int index = 0 ;
 //當前view的寬
 private float mX;
 //當前view的高
 private float mY;
 //當前view垂直方向中線
 private float middleY;
 //行與行之間的間距
 private final static int DY = 80 ;
 //歌詞文字大小
 private int mTextSize = 35;
 //歌詞中間字體的大小
 private int mBigTextSize = 45;
 //當前是否按下
 private boolean isTouch = false ;
 //上一次觸摸view的y軸坐標
 private float mLastY;
 //是否正在滑動
 private boolean isMoving;
 //記錄上一次滑動的時間
 private long lastMoveTime;
 //滑動速度追蹤類
 private VelocityTracker mVelocityTracker;
 //滑動最大速度
 private int mMaximumVelocity;
 //歌詞是否為空
 private boolean isEmpty;

 public VerticalScrollTextView(Context context) {
 this(context,null);
 }

 public VerticalScrollTextView(Context context, AttributeSet attrs) {
 this(context, attrs,0);
 }

 public VerticalScrollTextView(Context context, AttributeSet attrs, int defStyleAttr) {
 super(context, attrs, defStyleAttr);
 //獲取最大的滑動速度值
 mMaximumVelocity = ViewConfiguration.get(context)
  .getScaledMaximumFlingVelocity();
 init();
 }
 private void init(){
 setFocusable(true);
 setClickable(true);
 //歌詞為空設置默認值
 if(mDataList==null){
  mDataList = new ArrayList<>();
  Sentence sentence = new Sentence(0,"沒有獲取到歌詞",0);
  mDataList.add(sentence);
  isEmpty = true ;
 }
 //初始化歌詞畫筆
 mContentPaint = new Paint();
 mContentPaint.setTextSize(mTextSize);
 mContentPaint.setAntiAlias(true);
 mContentPaint.setColor(Color.parseColor("#e5e2e2"));
 //設置為serif字體
 mContentPaint.setTypeface(Typeface.SERIF);
 //設置字體為居中
 mContentPaint.setTextAlign(Paint.Align.CENTER);
 //初始化基線畫筆
 mLinePaint = new Paint();
 mLinePaint.setAntiAlias(true);
 mLinePaint.setStrokeWidth(1);
 mLinePaint.setColor(Color.WHITE);
 //進度背景顏色畫筆
 mRectPaint = new Paint();
 mLinePaint.setAntiAlias(true);
 mRectPaint.setColor(Color.parseColor("#66666666"));
 }

 @Override
 protected void onDraw(Canvas canvas) {
 super.onDraw(canvas);
 //如果當前進度為-1,直接返回,不用繪制
 if (index == -1)
  return;
 Sentence sentence = mDataList.get(index);
 //繪制中間行的歌詞,設置為高亮白色,大字體
 mContentPaint.setColor(Color.WHITE);
 mContentPaint.setTextSize(mBigTextSize);
 canvas.drawText(sentence.getName(), mX/2, middleY, mContentPaint);
 //當前為歌詞不為空並且按下的情況下,繪制基線和進度
 if(!isEmpty&&isTouch){
  //獲取中間行字體最高的位置
  float baseLine = middleY-Math.abs(mContentPaint.ascent());
  //繪制進度背景
  canvas.drawRect(10.0f,baseLine-70,150.0f,baseLine,mRectPaint);
  //繪制基線
  canvas.drawLine(10.0f,baseLine,mX-10,baseLine,mLinePaint);
  //設置進度字體大小
  mContentPaint.setTextSize(mTextSize);
  //繪制進度字體
  canvas.drawText(String.valueOf(index),85,baseLine-35,mContentPaint);
 }
 //初始化isEmpty
 isEmpty = false ;
 //初始化歌詞內容畫筆
 mContentPaint.setColor(Color.parseColor("#e5e2e2"));
 mContentPaint.setTextSize(mTextSize);
 //暫時保存中間線位置,來繪制中間線以上的行數字體
 float tempY = middleY;
 //繪制中間線以上的歌詞
 for (int i = index - 1; i >= 0; i--) {
  tempY = tempY - DY;
  if (tempY < 0) {
  break;
  }
  Sentence preSentence = mDataList.get(i);
  canvas.drawText(preSentence.getName(), mX/2, tempY, mContentPaint);
 }
 tempY = middleY;
 //繪制中間線以下的歌詞
 for (int i = index + 1; i < mDataList.size(); i++) {
  tempY = tempY + DY;
  if (tempY > mY) {
  break;
  }
  Sentence nexeSentence = mDataList.get(i);
  canvas.drawText(nexeSentence.getName(), mX/2, tempY, mContentPaint);
 }
 //初始化isMoving,到這裡表示滑動結束
 isMoving = false ;
 }
 protected void onSizeChanged(int w, int h, int ow, int oh) {
 super.onSizeChanged(w, h, ow, oh);
 //獲取view的寬和高
 mX = w;
 mY = h;
 middleY = h * 0.5f;
 }
 public long updateIndex(int index) {
 if (index == -1)
  return -1;
 this.index=index;
 return index;
 }
 public List<Sentence> getDataList() {
 return mDataList;
 }
 public void setDataList(List<Sentence> mDataList){
 this.mDataList = mDataList ;
 }
 public void updateUI(){
 new Thread(this).start();
 }

 @Override
 public boolean onTouchEvent(MotionEvent event) {
 int action = event.getAction();
 switch (action){
  case MotionEvent.ACTION_DOWN:
  isTouch =true;
  mLastY = event.getY();
  break;
  case MotionEvent.ACTION_MOVE:
  //創建速度追蹤器
  initVelocityTrackerIfNotExists();
  mVelocityTracker.addMovement(event);
  mVelocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
  //獲取當前速度。默認為100
  float velocity = mVelocityTracker.getYVelocity()==0?100:mVelocityTracker.getYVelocity();
  long currentTime = System.currentTimeMillis();
  //設置一個固定值和速度結合決定滑動更新的快慢
  if(!isMoving&¤tTime-lastMoveTime>20000/Math.abs(velocity)){
   isMoving = true ;
   lastMoveTime = System.currentTimeMillis();
   float currentY = event.getY();
   float mMoveY = currentY - mLastY;
   //向上滑動-1向下滑動+1
   int newIndex = mMoveY>0?index - 1:index+1;
   //循環滾動
   newIndex=newIndex<0?mDataList.size()-1:newIndex>=mDataList.size()?0:newIndex;
   updateIndex(newIndex);
   invalidate();
   mLastY = currentY;
  }
  break;
  case MotionEvent.ACTION_UP:
  isTouch = false ;
  recycleVelocityTracker();
  break;
 }
 return super.onTouchEvent(event);
 }

 @Override
 public void run() {
 //自動滾動刷新的時間間隔
 long time = 1000;
 //控制進度
 int i=0;
 while (true) {
  //當前不在按下的情況下自動滾動
  if(!isTouch){
  //設置當前的進度值
  long sleeptime = updateIndex(i);
  //使用handle刷新ui
  mHandler.post(mUpdateResults);
  if (sleeptime == -1)
   return;
  try {
   Thread.sleep(time);
   i++;
   //當到了最後一行的時候自動跳轉到第一行
   if(i==getDataList().size())
   i=0;
  } catch (InterruptedException e) {
   e.printStackTrace();
  }
  }
 }
 }
 Handler mHandler = new Handler();
 Runnable mUpdateResults = new Runnable() {
 public void run() {
  invalidate();
 }
 };
 //創建速度追蹤器
 private void initVelocityTrackerIfNotExists() {
 if (mVelocityTracker == null) {
  mVelocityTracker = VelocityTracker.obtain();
 }
 }
 //釋放
 private void recycleVelocityTracker() {
 if (mVelocityTracker != null) {
  mVelocityTracker.recycle();
  mVelocityTracker = null;
 }
 }
}

自定義view基本就是這樣了,我們可以把要定義的一些屬性寫在attrs裡面了,這裡就懶得寫了。大概的思路就是先繪制指定的index行的歌詞,然後繪制index上面行的歌詞,然後繪制index下面行的歌詞。然後新建一個線程,讓它通過handle隔一定的時間定時刷新歌詞行數。然後在onTouchEvent處理觸摸滾動行數,獲取到當前滾動速度來決定一個更新的時間間隔。從而實現觸摸滾動刷新的快慢。基本上就是這樣了。其他的看注釋。

再看下初始化數據測試的Activity:

VerticalScrollTextActivity.class

public class VerticalScrollTextActivity extends Activity {
 VerticalScrollTextView mSampleView;
 String[] str = {"你在南方的艷陽裡 大雪紛飛",
  "我在北方的寒夜裡 四季如春",
  "如果天黑之前來的及",
  "我要忘了你的眼睛",
  "窮極一生 做不完一場夢",
  "他不在和誰談論相逢的孤島",
  "因為心裡早已荒無人煙",
  "他的心裡在裝不下一個家",
  "做一個只對自己說謊的啞巴",
  "他說你任何為人稱道的美麗",
  "不及他第一次遇見你",
  "時光苟延殘喘 無可奈何",
  "如果所有土地連在一起",
  "走上一生只為擁抱你",
  "喝醉了他的夢 晚安",
  "你在南方的艷陽裡 大雪紛飛",
  "我在北方的寒夜裡 四季如春",
  "如果天黑之前來的及",
  "我要忘了你的眼睛",
  "窮極一生 做不完一場夢",
  "他不在和誰談論相逢的孤島",
  "因為心裡早已荒無人煙",
  "他的心裡在裝不下一個家",
  "做一個只對自己說謊的啞巴",
  "他說你任何為人稱道的美麗",
  "不及他第一次遇見你",
  "時光苟延殘喘 無可奈何",
  "如果所有土地連在一起",
  "走上一生只為擁抱你",
  "喝醉了他的夢 晚安"
 };
 @Override
 public void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 mSampleView = (VerticalScrollTextView) findViewById(R.id.sampleView1);
 List lst=new ArrayList<>();
 for(int i=0;i<str.length;i++){
  Sentence sen=new Sentence(i,str[i],i+1202034);
  lst.add(i, sen);
 }
 mSampleView.setDataList(lst);
 mSampleView.updateUI();
 } 
}

模擬了一首歌詞數據,然後setDataList,在調用updateUI()就行了。

最後看下布局文件

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="match_parent"
 android:layout_height="match_parent">

 <com.goach.lib.VerticalScrollTextView
 android:id="@+id/sampleView1"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:background="@drawable/bg"
 />
</RelativeLayout>

測試下,我們就可以看到效果了:

 

源碼下載:Android仿天天動聽歌曲自動滾動

以上就是本文的全部內容,希望對大家學習Android軟件編程有所幫助。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved