标签:
实现功能:滑动中...
滑动后
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,保存进度值 } }
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.iwanghang.drmplayer"> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.INTERNET" /> <application android:name=".DRMPlayerApp" android:allowBackup="true" android:icon="@mipmap/app_icon" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".SplashActivity" android:configChanges="orientation|keyboardHidden|screenSize" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".MainActivity" /> <activity android:name=".PlayActivity"/> <service android:name=".PlayService" android:enabled="true" android:exported="true" /> <!--<activity--> <!--android:name=".PlayActivity"--> <!--android:label="@string/title_activity_play"></activity>--> </application> </manifest>
/* * Copyright (C) 2013 Andreas Stuetz <andreas.stuetz@gmail.com> * * 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中 恢复状态值 } }
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添加以下代码: *<service *android:name=".PlayService" *android:enabled="true" *android:exported="true"> *</service> * * 实现功能(播放模式play_mode): * 顺序播放 * 随机播放 * 单曲循环 */ public class PlayService extends Service implements OnCompletionListener,OnErrorListener{ private MediaPlayer mPlayer; private int currentPosition;//当前正在播放的歌曲的位置 ArrayList<Mp3Info> 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()){ Mp3Info mp3Info = mp3Infos.get(position);//获取mp3Info对象 //进行播放,播放前判断 try { mPlayer.reset();//重启 mPlayer.setDataSource(this, Uri.parse(mp3Info.getUrl()));//资源解析,Mp3地址 mPlayer.prepare();//准备 mPlayer.start();//开始(播放) currentPosition = position;//保存当前位置到currentPosition,比如第一首,currentPosition = 1 } catch (IOException e) { e.printStackTrace(); } if(musicUpdatrListener!=null){ musicUpdatrListener.onChange(currentPosition);//更新当前位置 } } } //暂停 public void pause(){ if (mPlayer.isPlaying()){ mPlayer.pause(); isPause = true; } } //下一首 public void next(){ if (currentPosition>=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; } }
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; } }
<?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" android:padding="@dimen/activity_horizontal_margin"> <!--<android.support.v4.view.ViewPager--> <RelativeLayout android:id="@+id/viewpager" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_alignParentStart="true" android:layout_gravity="center" android:layout_above="@+id/linearLayout3"> <!-- 自定义滑动页面类的 --> <com.iwanghang.drmplayer.custom.FlingGalleryView android:id="@+id/fgv_player_main" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_centerInParent="true" > <include android:id="@+id/album_image_layout" layout="@layout/album_image_layout" /> <include android:id="@+id/lrc_layout" layout="@layout/lrc_layout" /> </com.iwanghang.drmplayer.custom.FlingGalleryView> </RelativeLayout> <!--</android.support.v4.view.ViewPager>--> <LinearLayout android:id="@+id/linearLayout3" android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="10dp" android:orientation="vertical" android:layout_alignParentBottom="true"> <LinearLayout android:id="@+id/linearLayout" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="10dp" android:orientation="horizontal"> <TextView android:id="@+id/textView1_start_time" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="00:00" android:textColor="@android:color/darker_gray" /> <SeekBar android:id="@+id/seekBar1" android:layout_width="235dp" android:layout_height="wrap_content" android:indeterminate="false"/> <TextView android:id="@+id/textView1_end_time" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="00:00" android:textColor="@android:color/darker_gray" /> </LinearLayout> <RelativeLayout android:id="@+id/linearLayout2" android:layout_width="match_parent" android:layout_height="50dp" android:layout_marginBottom="10dp" android:orientation="horizontal"> <ImageView android:id="@+id/imageView1_play_mode" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@mipmap/app_music_order" android:layout_alignBottom="@+id/imageView3_previous" android:layout_alignParentStart="true" /> <ImageView android:id="@+id/imageView3_previous" android:layout_width="50dp" android:layout_height="50dp" android:layout_alignTop="@+id/imageView2_play_pause" android:layout_toLeftOf="@+id/imageView2_play_pause" android:src="@mipmap/app_music_previous" /> <ImageView android:id="@+id/imageView2_play_pause" android:layout_width="50dp" android:layout_height="50dp" android:layout_alignTop="@+id/imageView1_next" android:layout_toLeftOf="@+id/imageView1_next" android:src="@mipmap/app_music_play" /> <ImageView android:id="@+id/imageView1_next" android:layout_width="50dp" android:layout_height="50dp" android:layout_alignParentBottom="true" android:layout_alignParentRight="true" android:src="@mipmap/app_music_next" /> </RelativeLayout> </LinearLayout> </RelativeLayout>
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:orientation="vertical" android:id="@+id/album_image_layout" android:weightSum="1"> <TextView android:id="@+id/textView1_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center" android:text="歌名" android:textAppearance="?android:attr/textAppearanceLarge" android:textColor="@android:color/holo_blue_light" android:layout_gravity="center_horizontal" android:layout_above="@+id/textView2" android:layout_centerHorizontal="true" /> <ImageView android:id="@+id/imageView1_ablum" android:layout_width="192dp" android:layout_height="192dp" android:scaleType="fitCenter" android:src="@mipmap/app_icon" android:layout_below="@+id/textView1_title" android:layout_centerHorizontal="true" android:layout_marginTop="10dp" android:layout_gravity="center_horizontal" /> <ImageView android:id="@+id/imageView1_ablum_reflection" android:layout_width="192dp" android:layout_height="96dp" android:layout_gravity="center" android:layout_below="@+id/imageView1_ablum" android:layout_centerHorizontal="true" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignLeft="@+id/seekBar1" android:layout_alignParentRight="true" android:layout_alignParentTop="true" android:layout_marginTop="20dp" android:gravity="center" android:text="向右滑动查看歌词 → " android:textColor="@android:color/holo_blue_light" android:layout_gravity="center_horizontal" android:id="@+id/textView2" /> </LinearLayout>
<?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" android:orientation="vertical" android:id="@+id/lrc_layout"> <TextView android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="歌词" android:textSize="@android:dimen/app_icon_size" android:layout_centerVertical="true" android:layout_centerHorizontal="true" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center" android:text="← 向左滑动查看专辑封面" android:textColor="@android:color/holo_blue_light" android:layout_gravity="center_horizontal" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" /> </RelativeLayout>
Android开发本地及网络Mp3音乐播放器(八)状态存取与界面滑动
标签:
原文地址:http://blog.csdn.net/iwanghang/article/details/51252882