Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android開發本地及網絡Mp3音樂播放器(八)狀態存取與界面滑動

Android開發本地及網絡Mp3音樂播放器(八)狀態存取與界面滑動

編輯:關於Android編程

實現功能:
退出應用時,保存歌曲位置(也就是當前是第幾首歌曲)
退出應用時,保存播放模式(也就是用戶設置的是順序播放/隨機播放/單曲循環)
進入應用時,讀取歌曲位置
進入應用時,讀取播放模式
實現PlayActivity(獨立音樂播放界面)的專輯圖片layout滑動,滑動後展示歌詞layout
(目前源碼,只實現了專輯圖片layout與歌詞layout,歌詞layout的信息填充後續會進行補充)

歡迎移動開發愛好者交流:我的微信是iwanghang

另外,我打算開始找工作,如果沈陽或周邊城市公司有意,也請與我聯系。

實現效果如圖:

\

滑動中...

\

滑動後

\


狀態存取實現代碼如下:
DRMPlayerApp如下:
package com.iwanghang.drmplayer;

import android.app.Application;
import android.content.Context;
import android.content.SharedPreferences;

import com.iwanghang.drmplayer.utils.Contant;

/**
 * Created by iwanghang on 16/4/26.
 */
public class DRMPlayerApp extends Application{

    //SharedPreferences是Android平台上一個輕量級的存儲類,用來保存應用的一些常用配置
    public static SharedPreferences sp;//SharedPreferences 直譯為 共享偏好

    @Override
    public void onCreate() {
        super.onCreate();

        //實例化,存儲名稱為SP_NAME,存儲模式為私有
        sp = getSharedPreferences(Contant.SP_NAME, Context.MODE_PRIVATE);
        //目的,比如在退出Activity時,保存循環模式,歌曲位置(第幾首歌曲)
        //這裡,我在MainActivity的onDestroy時,調用SharedPreferences,保存進度值
    }
}


AndroidManiFest如下:



    
    
    

    
        
            
                

                
            
        
        
        

        

        
            
    



MainActivity如下:
/*
 * Copyright (C) 2013 Andreas Stuetz 
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.iwanghang.drmplayer;

import android.content.SharedPreferences;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.graphics.drawable.TransitionDrawable;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.util.TypedValue;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ListView;

import com.astuetz.PagerSlidingTabStrip;
import com.astuetz.viewpager.extensions.sample.QuickContactFragment;
import com.iwanghang.drmplayer.utils.MediaUtils;
import com.iwanghang.drmplayer.vo.Mp3Info;

import java.nio.charset.CoderMalfunctionError;
import java.util.ArrayList;


public class MainActivity extends BaseActivity {

//	private final Handler handler = new Handler();

	private PagerSlidingTabStrip tabs;
	private ViewPager pager;
	private MyPagerAdapter adapter;



//	private Drawable oldBackground = null;
//	private int currentColor = 0xFFC74B46;

	private MyMusicListFragment myMusicListFragment;
	private NetMusicListFragment netMusicListFragment;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		tabs = (PagerSlidingTabStrip) findViewById(R.id.tabs);
		pager = (ViewPager) findViewById(R.id.pager);
		adapter = new MyPagerAdapter(getSupportFragmentManager());

		pager.setAdapter(adapter);

		final int pageMargin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 4, getResources()
				.getDisplayMetrics());
		pager.setPageMargin(pageMargin);

		tabs.setViewPager(pager);

		//修改主界面顏色,稍後修復功能,暫時使用默認顏色
		//changeColor(currentColor);

		//綁定服務
		//服務在加載SplashActivity(歡迎頁面)的時候,已經啟動
		//bindPlayService();
		//這裡,我在MyMusicListFragment裡面綁定,而沒有在MainActivity裡綁定

    }

	@Override
	public void publish(int progress) {
		//更新進度條
	}

	@Override
	public void change(int position) {
		//更新歌曲位置.按鈕的狀態等信息
		/**
		 * 本地音樂的播放UI實際上在MyMusicListFragment中,所以要
		 * 先在MyMusicListFragmen中,寫入public void changeUIStatus(int position){}
		 * 然後,傳參過去
		 */
		if (pager.getCurrentItem()==0){//如果頁面等於0,則說明選中的是第一個頁面,我的音樂頁面
			myMusicListFragment.changeUIStatusOnPlay(position);
		}else if (pager.getCurrentItem()==1){

		}
	}


//	@Override
//	public boolean onCreateOptionsMenu(Menu menu) {
//		getMenuInflater().inflate(R.menu.main, menu);
//		return true;
//	}
//
//	@Override
//	public boolean onOptionsItemSelected(MenuItem item) {
//
//		switch (item.getItemId()) {
//
//		case R.id.action_contact:
//			QuickContactFragment dialog = new QuickContactFragment();
//			dialog.show(getSupportFragmentManager(), "QuickContactFragment");
//			return true;
//
//		}
//
//		return super.onOptionsItemSelected(item);
//	}

//	private void changeColor(int newColor) {
//
//		tabs.setIndicatorColor(newColor);
//
//		// change ActionBar color just if an ActionBar is available
//		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
//
//			Drawable colorDrawable = new ColorDrawable(newColor);
//			Drawable bottomDrawable = getResources().getDrawable(R.drawable.actionbar_bottom);
//			LayerDrawable ld = new LayerDrawable(new Drawable[] { colorDrawable, bottomDrawable });
//
//			if (oldBackground == null) {
//
//				if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
//					ld.setCallback(drawableCallback);
//				} else {
//					getActionBar().setBackgroundDrawable(ld);
//				}
//
//			} else {
//
//				TransitionDrawable td = new TransitionDrawable(new Drawable[] { oldBackground, ld });
//
//				// workaround for broken ActionBarContainer drawable handling on
//				// pre-API 17 builds
//				// https://github.com/android/platform_frameworks_base/commit/a7cc06d82e45918c37429a59b14545c6a57db4e4
//				if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
//					td.setCallback(drawableCallback);
//				} else {
//					getActionBar().setBackgroundDrawable(td);
//				}
//
//				td.startTransition(200);
//
//			}
//
//			oldBackground = ld;
//
//			// http://stackoverflow.com/questions/11002691/actionbar-setbackgrounddrawable-nulling-background-from-thread-handler
//			getActionBar().setDisplayShowTitleEnabled(false);
//			getActionBar().setDisplayShowTitleEnabled(true);
//
//		}
//
//		currentColor = newColor;
//
//	}

//	public void onColorClicked(View v) {
//
//		int color = Color.parseColor(v.getTag().toString());
//		changeColor(color);
//
//	}

//	@Override
//	protected void onSaveInstanceState(Bundle outState) {
//		super.onSaveInstanceState(outState);
//		outState.putInt("currentColor", currentColor);
//	}

//	@Override
//	protected void onRestoreInstanceState(Bundle savedInstanceState) {
//		super.onRestoreInstanceState(savedInstanceState);
//		currentColor = savedInstanceState.getInt("currentColor");
//		changeColor(currentColor);
//	}

//	private Drawable.Callback drawableCallback = new Drawable.Callback() {
//		@Override
//		public void invalidateDrawable(Drawable who) {
//			getActionBar().setBackgroundDrawable(who);
//		}
//
//		@Override
//		public void scheduleDrawable(Drawable who, Runnable what, long when) {
//			handler.postAtTime(what, when);
//		}
//
//		@Override
//		public void unscheduleDrawable(Drawable who, Runnable what) {
//			handler.removeCallbacks(what);
//		}
//	};



	public class MyPagerAdapter extends FragmentPagerAdapter {

		private final String[] TITLES = { "我的音樂", "網絡音樂"};

		public MyPagerAdapter(FragmentManager fm) {
			super(fm);
		}

		@Override
		public CharSequence getPageTitle(int position) {
			return TITLES[position];
		}

		@Override
		public int getCount() {
			return TITLES.length;
		}

		@Override
		public Fragment getItem(int position) {
			//return SuperAwesomeCardFragment.newInstance(position);
			if(position==0){
				if(myMusicListFragment==null){
					myMusicListFragment = MyMusicListFragment.newInstance();
				}
				return myMusicListFragment;
			}else if (position == 1){
				if(netMusicListFragment==null){
					netMusicListFragment = netMusicListFragment.newInstance();
				}
				return netMusicListFragment;
			}
			return null;
			//return MyMusicListFragment.newInstance();
		}

	}

	@Override
	protected void onDestroy() {
		super.onDestroy();
		//保存當前播放的一些狀態值
		DRMPlayerApp app = (DRMPlayerApp) getApplication();
		SharedPreferences.Editor editor = app.sp.edit();
		//保存 當前正在播放的歌曲的位置
		editor.putInt("currentPosition",playService.getCurrentPosition());
		//保存 播放模式
		editor.putInt("play_mode",playService.getPlay_mode());
		//保存 提交
		editor.commit();

		//創建DRMPlayerApp繼承Application,同時需要在把AndroidManiFest中的public換成DRMPlayerApp
		//在DRMPlayerApp的onCreate中 實例化 SharedPreferences
		//在MainActivity的onDestroy中 保存狀態值
		//在PlayService的onCreate中 恢復狀態值
	}
}




PlayService如下:
package com.iwanghang.drmplayer;

import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnErrorListener;
import android.net.Uri;
import android.os.Binder;
import android.os.IBinder;

import com.iwanghang.drmplayer.utils.MediaUtils;
import com.iwanghang.drmplayer.vo.Mp3Info;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 音樂播放的服務組件
 * 實現功能:
 * 播放
 * 暫停
 * 下一首
 * 上一首
 * 獲取當前歌曲的播放進度
 *
 * 需要在AndroidManifest.xml添加以下代碼:
 *
 *
 *
 * 實現功能(播放模式play_mode):
 * 順序播放
 * 隨機播放
 * 單曲循環
 */
public class PlayService extends Service implements OnCompletionListener,OnErrorListener{
    private MediaPlayer mPlayer;
    private int currentPosition;//當前正在播放的歌曲的位置
    ArrayList mp3Infos;

    private MusicUpdatrListener musicUpdatrListener;

    //創建一個單實力的線程,用於更新音樂信息
    private ExecutorService es = Executors.newSingleThreadExecutor();

    //播放模式
    public static final int ORDER_PLAY = 1;//順序播放
    public static final int RANDOM_PLAY = 2;//隨機播放
    public static final int SINGLE_PLAY = 3;//單曲循環
    private int play_mode = ORDER_PLAY;//播放模式,默認為順序播放

    /**
     * @param play_mode
     * ORDER_PLAY = 1;//順序播放
     * RANDOM_PLAY = 2;//隨機播放
     * SINGLE_PLAY = 3;//單曲循環
     */
    //set方法
    public void setPlay_mode(int play_mode) {
        this.play_mode = play_mode;
    }

    //get方法
    public int getPlay_mode() {
        return play_mode;
    }

    private boolean isPause = false;//歌曲播放中的暫停狀態

    public boolean isPause(){
        return isPause;
    }

    public PlayService() {
    }

    public int getCurrentPosition(){
        return currentPosition;
    }

    private Random random = new Random();//創建隨機對象
    //MediaPlayer.Completion 播放完成 實現播放下一首功能
    //播放完成以後,判斷播放模式(曲目循環方式)
    //為了實現循環後,可以顯示音樂信息,需要在PlayAcivity的change裡添加對應代碼
    @Override
    public void onCompletion(MediaPlayer mp) {
        switch (play_mode){
            case ORDER_PLAY://順序播放
                next();//下一首
                break;
            case RANDOM_PLAY://隨機播放
                //currentPosition = random.nextInt(mp3Infos.size());//隨機下標為mp3Infos.size()
                //play(currentPosition);
                play(random.nextInt(mp3Infos.size()));
                break;
            case SINGLE_PLAY://單曲循環
                play(currentPosition);
                break;
            default:
                break;
        }
    }
    //MediaPlayer.Error 播放錯誤 處理實現播放下一首功能出現的錯誤
    @Override
    public boolean onError(MediaPlayer mp, int what, int extra) {
        mp.reset();//重啟
        return false;
    }

    //內部類PlayBinder實現Binder,得到當前PlayService對象
    class PlayBinder extends Binder{
        public PlayService getPlayService(){
            System.out.println("PlayService #1 " + PlayService.this);
            return PlayService.this;
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        return new PlayBinder();//通過PlayBinder拿到PlayService,給Activity調用
    }

    @Override
    public void onCreate() {
        super.onCreate();

        //恢復狀態值
        DRMPlayerApp app = (DRMPlayerApp) getApplication();
        currentPosition = app.sp.getInt("currentPosition",0);
        play_mode = app.sp.getInt("play_mode",PlayService.ORDER_PLAY);
        //創建DRMPlayerApp繼承Application,同時需要在把AndroidManiFest中的public換成DRMPlayerApp
        //在DRMPlayerApp的onCreate中 實例化 SharedPreferences
        //在MainActivity的onDestroy中 保存狀態值
        //在PlayService的onCreate中 恢復狀態值

        mPlayer = new MediaPlayer();
        mPlayer.setOnCompletionListener(this);//注冊播放完成事件
        mPlayer.setOnErrorListener(this);//注冊播放錯誤事件
        mp3Infos = MediaUtils.getMp3Infos(this);//獲取Mp3列表
        es.execute(updateSteatusRunnable);//更新進度值
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        //回收線程
        if (es!=null && !es.isShutdown()){//當進度值等於空,並且,進度值沒有關閉
            es.shutdown();
            es = null;
        }
    }

    //利用Runnable來實現多線程
    /**
     * Runnable
     * Java中實現多線程有兩種途徑:繼承Thread類或者實現Runnable接口.
     * Runnable接口非常簡單,就定義了一個方法run(),繼承Runnable並實現這個
     * 方法就可以實現多線程了,但是這個run()方法不能自己調用,必須由系統來調用,否則就和別的方法沒有什麼區別了.
     * 好處:數據共享
     */
    Runnable updateSteatusRunnable = new Runnable() {//更新狀態
        @Override
        public void run() {
            //不斷更新進度值
            while (true){
                //音樂更新監聽不為空,並且,媒體播放不為空,並且媒體播放為播放狀態
                if(musicUpdatrListener!=null && mPlayer!=null && mPlayer.isPlaying()){
                    musicUpdatrListener.onPublish(getCurrentProgress());//獲取當前的進度值
                }
                try {
                    Thread.sleep(500);//500毫秒更新一次
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    };

    //播放
    public void play(int position){
        if (position>=0 && position=mp3Infos.size()-1){//如果超出最大值,(因為第一首是0),說明已經是最後一首
            currentPosition = 0;//回到第一首
        }else {
            currentPosition++;//下一首
        }
        play(currentPosition);
    }

    //上一首 previous
    public void prev(){
        if (currentPosition-1<0){//如果上一首小於0,說明已經是第一首
            currentPosition = mp3Infos.size()-1;//回到最後一首
        }else {
            currentPosition--;//上一首
        }
        play(currentPosition);
    }

    //默認開始播放的方法
    public void start(){
        if (mPlayer!=null && !mPlayer.isPlaying()){//判斷當前歌曲不等於空,並且沒有在播放的狀態
            mPlayer.start();

        }
    }

    //獲取當前是否為播放狀態,提供給MyMusicListFragment的播放暫停按鈕點擊事件判斷狀態時調用
    public boolean isPlaying(){
        if (mPlayer!=null){
            return mPlayer.isPlaying();
        }
        return false;
    }

    //獲取當前的進度值
    public int getCurrentProgress(){
        if(mPlayer!=null && mPlayer.isPlaying()){//mPlayer不為空,並且,為播放狀態
            return mPlayer.getCurrentPosition();
        }
        return 0;
    }
    //getDuration 獲取文件的持續時間
    public int getDuration(){
        return mPlayer.getDuration();
    }
    //seekTo 尋找指定的時間位置 (跳到某個時間點進行播放)
    public void seekTo(int msec){
        mPlayer.seekTo(msec);
    }

    //更新狀態的接口(PlayService的內部接口),並在BaseActivity中實現
    public interface MusicUpdatrListener{//音樂更新監聽器
        public void onPublish(int progress);//發表進度事件(更新進度條)
        public void onChange(int position); //更新歌曲位置.按鈕的狀態等信息
        //聲明MusicUpdatrListener後,添加set方法
    }

    //set方法
    public void setMusicUpdatrListener(MusicUpdatrListener musicUpdatrListener) {
        this.musicUpdatrListener = musicUpdatrListener;
    }
}


——————————我是分割線——————————
界面滑動實現代碼如下:
FlingGalleryView如下:
package com.iwanghang.drmplayer.custom;

import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.Scroller;

import com.iwanghang.drmplayer.R;

/**
 * Created by iwanghang on 16/4/26.
 */
public class FlingGalleryView extends ViewGroup {

    private static final int SNAP_VELOCITY = 1000;
    // 記錄當前屏幕下標,取值范圍是:0 到 getChildCount()-1
    private int mCurrentScreen;
    private Scroller mScroller;
    // 速度追蹤器,主要是為了通過當前滑動速度判斷當前滑動是否為fling
    private VelocityTracker mVelocityTracker;
    // 記錄滑動時上次手指所處的位置
    private float mLastMotionX;
    private float mLastMotionY;
    // Touch狀態值 0:靜止 1:滑動
    private final static int TOUCH_STATE_REST = 0;
    private final static int TOUCH_STATE_SCROLLING = 1;
    // 記錄當前touch事件狀態--滑動(TOUCH_STATE_SCROLLING)、靜止(TOUCH_STATE_REST 默認)
    private int mTouchState = TOUCH_STATE_REST;
    // 記錄touch事件中被認為是滑動事件前的最大可滑動距離
    private int mTouchSlop;
    // 手指拋動作的最大速度px/s 每秒多少像素
    private int mMaximumVelocity;
    // 滾動到指定屏幕的事件
    private OnScrollToScreenListener mScrollToScreenListener;
    // 自定義touch事件
    private OnCustomTouchListener mCustomTouchListener;
    //滾動到每個屏幕時是否都要觸發OnScrollToScreenListener事件
    private boolean isEveryScreen=false;

    public FlingGalleryView(Context context) {
        super(context);
        init();
        mCurrentScreen = 0;
    }

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

    public FlingGalleryView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);

        TypedArray a = context.obtainStyledAttributes(attrs,R.styleable.FlingGalleryView, defStyle, 0);
        mCurrentScreen = a.getInt(R.styleable.FlingGalleryView_defaultScreen, 0);
        a.recycle();
        init();
    }

    private void init() {
        mScroller = new Scroller(getContext());
        final ViewConfiguration configuration = ViewConfiguration
                .get(getContext());
        mTouchSlop = configuration.getScaledTouchSlop();
        mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
    }

    // 保證在同一個屏幕執行一下切屏事件的一些參數
    private int count = -1;
    private int defaultScreen = -1;

    // 當滾動條滑動時調用,startScroll()設置的是參數,實際滑動,在其裡執行,
    @Override
    public void computeScroll() {
        // mScroller.computeScrollOffset計算當前新的位置,true表示還在滑動,仍需計算
        if (mScroller.computeScrollOffset()) {
            // 返回true,說明scroll還沒有停止
            scrollTo(mScroller.getCurrX(), 0);
            if(isEveryScreen)singleScrollToScreen();
            postInvalidate();
        }
    }

    // 保證在同一個屏幕執行一下切屏事件
    private void singleScrollToScreen() {
        final int screenWidth = getWidth();
        int whichScreen = (getScrollX() + (screenWidth / 2)) / screenWidth;
        if (whichScreen > (getChildCount() - 1)) {
            return;
        }
        if (defaultScreen == -1) {
            defaultScreen = whichScreen;
            count = 1;
        } else {
            if (defaultScreen == whichScreen && count == 0) {
                count = 1;
            } else {
                if (defaultScreen != whichScreen) {
                    defaultScreen = whichScreen;
                    count = 0;
                }
            }
        }
        if (count == 0) {
            if (mScrollToScreenListener != null) {
                mScrollToScreenListener.operation(whichScreen, getChildCount());
            }
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        final int width = MeasureSpec.getSize(widthMeasureSpec);
        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        if (widthMode != MeasureSpec.EXACTLY) {
            throw new IllegalStateException(
                    "Workspace can only be used in EXACTLY mode.");
        }
        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        if (heightMode != MeasureSpec.EXACTLY) {
            throw new IllegalStateException(
                    "Workspace can only be used in EXACTLY mode.");
        }
        final int count = getChildCount();
        for (int i = 0; i < count; i++) {
            getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);
        }
        scrollTo(mCurrentScreen * width, 0);
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right,
                            int bottom) {
        int childLeft = 0;
        // 橫向平鋪childView
        final int count = getChildCount();
        for (int i = 0; i < count; i++) {
            final View child = getChildAt(i);
            child.setOnTouchListener(childTouchListener);
            if (child.getVisibility() != View.GONE) {
                final int childWidth = child.getMeasuredWidth();
                child.layout(childLeft, 0, childLeft + childWidth,
                        child.getMeasuredHeight());
                childLeft += childWidth;
            }
        }
    }

    // 設定childView的Touch事件返回true,這樣可以在parentView中截獲touch(即onInterceptTouchEvent)的move,up等事件
    private OnTouchListener childTouchListener = new OnTouchListener() {
        public boolean onTouch(View v, MotionEvent event) {
            return true;
        }
    };

    // 在系統向該ViewGroup及其各個childView觸發onTouchEvent()之前對相關事件進行一次攔截
	/*
	 * down事件首先會傳遞到onInterceptTouchEvent()方法
	 * 如果該ViewGroup的onInterceptTouchEvent()在接收到down事件處理完成之後return
	 * false,那麼後續的move,
	 * up等事件將繼續會先傳遞給該ViewGroup,之後才和down事件一樣傳遞給最終的目標view的onTouchEvent()處理。
	 * 如果該ViewGroup的onInterceptTouchEvent()在接收到down事件處理完成之後return
	 * true,那麼後續的move,
	 * up等事件將不再傳遞給onInterceptTouchEvent(),而是和down事件一樣傳遞給該ViewGroup的onTouchEvent
	 * ()處理,注意,目標view將接收不到任何事件。
	 * 如果最終需要處理事件的view的onTouchEvent()返回了false,那麼該事件將被傳遞至其上一層次的view的onTouchEvent
	 * ()處理。 如果最終需要處理事件的view
	 * 的onTouchEvent()返回了true,那麼後續事件將可以繼續傳遞給該view的onTouchEvent()處理。
	 */
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        if (mCustomTouchListener != null) {
            mCustomTouchListener.operation(ev);
        }
        final int action = ev.getAction();
        if ((action == MotionEvent.ACTION_MOVE)
                && (mTouchState != TOUCH_STATE_REST)) {
            return true;
        }
        final float x = ev.getX();
        final float y = ev.getY();
        switch (action) {
            case MotionEvent.ACTION_MOVE:
                // 計算X方向移動的距離
                final int xDiff = (int) Math.abs(x - mLastMotionX);
                final int touchSlop = mTouchSlop;
                if (xDiff > touchSlop) {
                    // 移動方向小於45度時即X方向可以移動
                    if (Math.abs(mLastMotionY - y) / Math.abs(mLastMotionX - x) < 1) {
                        mTouchState = TOUCH_STATE_SCROLLING;
                    }
                }
                break;
            case MotionEvent.ACTION_DOWN:
                mLastMotionX = x;
                mLastMotionY = y;
                mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST
                        : TOUCH_STATE_SCROLLING;
                break;
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
                mTouchState = TOUCH_STATE_REST;
                break;
        }
        return mTouchState != TOUCH_STATE_REST;
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        if (mVelocityTracker == null) {
            mVelocityTracker = VelocityTracker.obtain();
        }
        mVelocityTracker.addMovement(ev);
        final int action = ev.getAction();
        final float x = ev.getX();
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                if (!mScroller.isFinished()) {
                    // 終止滾動條的滑動動畫
                    mScroller.abortAnimation();
                }
                mLastMotionX = x;
                count = -1;
                defaultScreen = -1;
                break;
            case MotionEvent.ACTION_MOVE:
                if (mTouchState == TOUCH_STATE_SCROLLING) {
                    final float t_width = (getWidth() / 4f);
                    // 最後一個屏幕向左移動時,不能超過屏幕的4分之一
                    if (getScrollX() > ((getChildCount() - 1) * getWidth() + t_width)) {
                        break;
                    }
                    // 第一個屏幕向右移動時,不能超過屏幕的4分之一
                    if (getScrollX() < ((t_width) * -1)) {
                        break;
                    }
                    final int deltaX = (int) (mLastMotionX - x);
                    mLastMotionX = x;
                    scrollBy(deltaX, 0);
                }
                break;
            case MotionEvent.ACTION_UP:
                if (mTouchState == TOUCH_STATE_SCROLLING) {
                    final VelocityTracker velocityTracker = mVelocityTracker;
                    velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);// 使用pix/s為單位
                    int velocityX = (int) velocityTracker.getXVelocity();
                    if (velocityX > SNAP_VELOCITY && mCurrentScreen > 0) {
                        // 向右移動
                        snapToScreen(mCurrentScreen - 1, false);
                    } else if (velocityX < -SNAP_VELOCITY
                            && mCurrentScreen < getChildCount() - 1) {
                        // 向左移動
                        snapToScreen(mCurrentScreen + 1, false);
                    } else {
                        snapToDestination();
                    }
                    if (mVelocityTracker != null) {
                        mVelocityTracker.recycle();
                        mVelocityTracker = null;
                    }
                }
                mTouchState = TOUCH_STATE_REST;
                break;
            case MotionEvent.ACTION_CANCEL:
                mTouchState = TOUCH_STATE_REST;
        }
        return true;
    }

    // 計算應該去哪個屏
    private void snapToDestination() {
        final int screenWidth = getWidth();
        // 如果超過屏幕的一半就算是下一個屏
        final int whichScreen = (getScrollX() + (screenWidth / 2))/ screenWidth;
        snapToScreen(whichScreen, false);
    }

    // 切換屏幕
    private void snapToScreen(int whichScreen, boolean isJump) {
        // 判斷下一個屏幕是否有效,並糾正
        whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1));
        if (getScrollX() != (whichScreen * getWidth())) {
            final int delta = whichScreen * getWidth() - getScrollX();
            count = -1;
            defaultScreen = -1;
            // 開始滾動動畫
            mScroller.startScroll(getScrollX(), 0, delta, 0,
                    Math.abs(delta) * 2);
            final int t_mCurrentScreen = mCurrentScreen;
            mCurrentScreen = whichScreen;
            // 判斷是否在同一個屏幕,不在則執行切換屏幕
            if (t_mCurrentScreen != whichScreen) {
                // 防止重復執行切換屏幕事件
                if (Math.abs(t_mCurrentScreen - whichScreen) == 1 && !isJump) {
                    doOnScrollToScreen();
                }
            }
            invalidate();
        }
    }

    private void doOnScrollToScreen() {
        if (mScrollToScreenListener != null) {
            mScrollToScreenListener.operation(mCurrentScreen, getChildCount());
        }
    }

    /**
     * 設置切換到的指定下標屏幕0至getChildCount()-1
     * */
    public void setToScreen(int whichScreen, boolean isAnimation) {
        if (isAnimation) {
            snapToScreen(whichScreen, true);
        } else {
            whichScreen = Math.max(0,
                    Math.min(whichScreen, getChildCount() - 1));
            mCurrentScreen = whichScreen;
            // 直接滾動到該位置
            scrollTo(whichScreen * getWidth(), 0);
            if (whichScreen != mCurrentScreen) {
                doOnScrollToScreen();
            }
            invalidate();
        }
    }

    /**
     * 設置默認屏幕的下標
     * */
    public void setDefaultScreen(int defaultScreen) {
        mCurrentScreen = defaultScreen;
    }

    /**
     * 獲取當前屏幕的下標
     * */
    public int getCurrentScreen() {
        return mCurrentScreen;
    }

    /**
     * 注冊滾動到指定屏幕的事件
     * */
    public void setOnScrollToScreenListener(
            OnScrollToScreenListener scrollToScreenListener) {
        if (scrollToScreenListener != null) {
            this.mScrollToScreenListener = scrollToScreenListener;
        }
    }

    /**
     * 注冊自定義Touch事件
     * */
    public void setOnCustomTouchListener(
            OnCustomTouchListener customTouchListener) {
        if (customTouchListener != null) {
            this.mCustomTouchListener = customTouchListener;
        }
    }

    /**
     * 滾動到指定屏幕的事件(即切屏事件)
     * */
    public interface OnScrollToScreenListener {
        public void operation(int currentScreen, int screenCount);
    }

    /**
     * 自定義的一個Touch事件
     * */
    public interface OnCustomTouchListener {
        public void operation(MotionEvent event);
    }

    /**
     * 滾動到每個屏幕時是否都要觸發OnScrollToScreenListener事件
     * */
    public void setEveryScreen(boolean isEveryScreen) {
        this.isEveryScreen = isEveryScreen;
    }
}


activity_music_play如下:



    
        

            

            
        


    
    

    

        

            

            

            

        

        

            

            

            

            

        

    




album_image_layout如下:



    

    

    

    



lrc_layout如下:



    

    






重點說明:
SharedPreferences是Android平台上一個輕量級的存儲類,用來保存應用的一些常用配置
在DRMPlayerApp中,實例化SharedPreferences
//創建DRMPlayerApp繼承Application,同時需要在把AndroidManiFest中的public換成DRMPlayerApp
//在DRMPlayerApp的onCreate中 實例化 SharedPreferences
//在MainActivity的onDestroy中 保存狀態值
//在PlayService的onCreate中 恢復狀態值
實現展示歌詞信息,修改了PlayActivity(獨立音樂播放界面),在activity_music_play.xml中include了2個layout:album_image_layout和lrc_layout
實現滑動功能,直接拿了一段代碼FlingGalleryView,暫時不求甚解的用了,以後慢慢學習。
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved