videoview 横竖屏切换

Android videoview 横竖屏切换,主要是对各种状态封装了一下,以后移植不需要考虑这些状态。然后横竖屏,对布局结构有点限制,要处于上层才能全屏
public class MainActivity extends Activity {

    public static final String URL_VIDEO = "http://static.wezeit.com/o_1a9jjk9021fkt7vs1mlo16i91gvn9.mp4";
    @Bind(R.id.webview)
    public WebView mWebView;

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

        initView();
        initWidget();
        setupListener();
        loadData();
    }

    private void initView(){
        ButterKnife.bind(this);
        initFakeStatusBarHeight(true);
    }

    private void initWidget(){
        initWebViewSetting(mWebView);
    }

    private void setupListener(){
        setupWebViewListener();
    }

    private void loadData(){
        View view = findViewById(R.id.activity_video_rl);
        initVideoMode(view);
        String url = "http://www.wezeit.com/wap/297121.html";
        loadWebviewUrl(url);
    }
    protected void loadWebviewUrl(String url){
        DebugTools.d("js2 discovery2 jump3 vote2 news2 current url: " + url);
        if(!TextUtils.isEmpty(url)){
            mWebView.loadUrl(url);
        }
    }

    protected int mPixelInsetTop;
    protected void initFakeStatusBarHeight(boolean isNewsPage){
        View statusbarBgLayout = (View)findViewById(R.id.statusbar_bg_layout);
        if(statusbarBgLayout == null){
            return;
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            mPixelInsetTop = CommonTools.getStatusbarHeight(this);
            RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams)statusbarBgLayout.getLayoutParams();
            params.height = mPixelInsetTop;
            statusbarBgLayout.setLayoutParams(params);
            statusbarBgLayout.setBackgroundResource(R.color.black);
        }else{
            mPixelInsetTop = 0;
            statusbarBgLayout.setVisibility(View.GONE);
        }
    }

    //----------videoview----------------
    private VideoViewHolder mVideoHolder;
    private VideoView mVideoView;
    private VideoViewHolderControl mVideoControl;
    private void initVideoMode(View view){
        showFullScreen(false);
        mVideoView = (VideoView) view.findViewById(R.id.videoview);
        mVideoHolder = new VideoViewHolder(view);
        mVideoHolder.imgIv.setImageResource(R.mipmap.index);
        mVideoControl = new VideoViewHolderControl(mVideoHolder, mVideoView, URL_VIDEO);
        setupVideoControlListener(mVideoControl);
        mVideoControl.setup();
        setVideoViewLayout(false);
    }

    private void setupVideoControlListener(VideoViewHolderControl control){
        control.setOnVideoControlListener(new VideoViewHolderControl.OnVideoControlProxy() {
            @Override
            public void onCompletion() {
                DebugTools.d("video2 onCompletion");
                setFullScreen(false);
            }

            @Override
            public void onClickHalfFullScreen() {
                boolean isFull = isFullScreen();
                setFullScreen(!isFull);
            }

            @Override
            public void onError(int code, String msg) {

            }

        });
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
            initHalfFullState(true);
        } else if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
            initHalfFullState(false);
        }
    }

    private void initHalfFullState(boolean isFull){
        DebugTools.d("video2 initHalfFullState isFull: " + isFull);
        setVideoViewLayout(isFull);
        showFullScreen(isFull);
    }


    //---------videoview fullscreen---------
    private void showFullScreen(boolean isFullScreen){
        if(isFullScreen){
//            //不显示程序的标题栏
            hideNavigationBar();
        }else{
            showNavigationBar();
        }
    }

    protected void setFullScreen(boolean isFull){
        if(isFull){
            if (getRequestedOrientation() != ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
            }
        }else{
            if (getRequestedOrientation() != ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) {
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
            }
        }
    }

    protected boolean isFullScreen(){
        return getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
    }

    private void showNavigationBar(){
        View decorView = getWindow().getDecorView();
        decorView.setSystemUiVisibility(
                View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                        | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
    }

    public void hideNavigationBar() {
        int uiFlags = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                | View.SYSTEM_UI_FLAG_FULLSCREEN; // hide status bar

        getWindow().getDecorView().setSystemUiVisibility(uiFlags);
    }

    private void setVideoViewLayout(boolean isFull){
        RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams)mVideoHolder.videoRl.getLayoutParams();
        RelativeLayout.LayoutParams controlParams = (RelativeLayout.LayoutParams)mVideoHolder.mediaControl.getLayoutParams();
        RelativeLayout.LayoutParams indexImageParams = (RelativeLayout.LayoutParams)mVideoHolder.imgIv.getLayoutParams();

        int videoMarginTop = (int)getResources().getDimension(R.dimen.library_video_video_margin_top) + mPixelInsetTop;
        if(isFull){
            params.height = LayoutParams.MATCH_PARENT;
            params.addRule(RelativeLayout.CENTER_IN_PARENT);
            params.setMargins(0, 0, 0, 0);

            controlParams.setMargins(0, 0, 0, 0);

            indexImageParams.height = LayoutParams.MATCH_PARENT;
            indexImageParams.setMargins(0, 0, 0, 0);
        }else{
            params.height = DisplayUtil.dip2px(this, 202);
            params.addRule(RelativeLayout.ALIGN_PARENT_TOP);
            params.setMargins(0, videoMarginTop, 0, 0);

            controlParams.setMargins(0, 0, 0, 0);

            indexImageParams.height = DisplayUtil.dip2px(this, 202);
            indexImageParams.setMargins(0, 0, 0, 0);

        }
        mVideoHolder.videoRl.setLayoutParams(params);
        mVideoHolder.mediaControl.setLayoutParams(controlParams);
        mVideoHolder.imgIv.setLayoutParams(indexImageParams);
    }


    //--------------webview--------------
    protected void initWebViewStorage(WebView webview){
        webview.getSettings().setDomStorageEnabled(true);
        webview.getSettings().setAppCacheMaxSize(1024*1024*8);
        String appCachePath = getApplicationContext().getCacheDir().getAbsolutePath();
        webview.getSettings().setAllowFileAccess(true);
        webview.getSettings().setAppCacheEnabled(true);

        webview.getSettings().setDatabaseEnabled(true);
        webview.getSettings().setDatabasePath(appCachePath);
    }

    protected void initWebViewSetting(WebView webview){
        WebSettings webseting = webview.getSettings();
//        webseting.setRenderPriority(RenderPriority.HIGH);
        webseting.setJavaScriptEnabled(true);
//      webseting.setPluginsEnabled(true);
        webseting.setSupportZoom(true);
        webview.setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY);

        webseting.setUseWideViewPort(true);
        webseting.setLoadWithOverviewMode(true);


        initWebViewStorage(webview);
    }

    protected void setupWebViewListener(){
        mWebView.setWebViewClient(new WebViewClient() {

        });
        mWebView.setWebChromeClient(new WebChromeClient() {
            @Override
            public void onProgressChanged(WebView view, int newProgress) {
                super.onProgressChanged(view, newProgress);
            }
        });
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event){
        if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_DOWN){
            handleClickBack();
            return true;
        }
        return super.onKeyDown(keyCode, event);
    }

    protected void handleClickBack(){
        if(isFullScreen()){
            setFullScreen(false);
            return;
        }
        if (mWebView != null){
            mWebView.onPause();
        }
        finish();
    }

    @Override
    protected void onDestroy() {
        ButterKnife.unbind(this);
        super.onDestroy();
    }
}

VideoViewHolderControl,状态控制器,里面集成了视频的基本操作

public class VideoViewHolderControl {

    private VideoViewHolder mHolder;
    private VideoView mVideoView;
    private String mUrl;
    private boolean mIsFullScreen = false;
    private boolean mIsFullScreenMode = false;
    public VideoViewHolderControl(VideoViewHolder holder, VideoView videoView, String url){
        mHolder = holder;       
        mVideoView = videoView;
        mUrl = url;
//        mUrl = "http://img.owspace.com/V_tnh2100_1453346133.2098116.m3u8";
    }

    public void setup(){
        setupPlayListener(mHolder);
        showInitStateState(mHolder);
    }

    public void play(){
        if(mHolder != null){
            handleStartPlayVideo(mHolder);
        }
    }

    public void setIsFullScreenMode(boolean isFullMode){
        mIsFullScreenMode = isFullMode;
        if(mIsFullScreenMode){
            if(mHolder != null){
                mHolder.halfFullIb.setImageResource(R.mipmap.library_video_mediacontroller_to_half);
                hideFullHalIb(mHolder);
            }
        }
    }

    private void hideFullHalIb(VideoViewHolder holder){
        if(holder == null){
            return;
        }

        holder.halfFullIb.setVisibility(View.GONE);
        RelativeLayout.LayoutParams totalTimeParams = (RelativeLayout.LayoutParams)holder.totalTimeTv.getLayoutParams();
        totalTimeParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT, RelativeLayout.TRUE);
        totalTimeParams.setMargins(0, 0, (int)DisplayUtil.dpToPx(10), 0);
        holder.totalTimeTv.setLayoutParams(totalTimeParams);
    }

    private void setupPlayListener(final VideoViewHolder holder){
        holder.playPauseIb.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                handleStartPlayVideo(holder);
            }
        });

        holder.imgIv.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                handleStartPlayVideo(holder);
            }
        });

        holder.playSymbolIv.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                handleStartPlayVideo(holder);
            }
        });

        if(holder.halfFullIb != null){
            holder.halfFullIb.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    if(mIsFullScreenMode){
                        if(mOnVideoControlListener != null){
                            mOnVideoControlListener.onClickHalfFullScreen();
                        }
                    }else{
                        if(mOnVideoControlListener != null){
                            mOnVideoControlListener.onClickHalfFullScreen();
                            mIsFullScreen = !mIsFullScreen;
                            setHalfFullScreenState(holder, mIsFullScreen);
                        }
                    }
                }
            });
        }

        if(holder.backIb != null){
            holder.backIb.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    if(mIsFullScreenMode){
                        if(mOnVideoControlListener != null){
                            mOnVideoControlListener.onClickHalfFullScreen();
                        }
                    }
                }
            });
        }
    }

    private void setHalfFullScreenState(VideoViewHolder holder, boolean isFull){
        if(isFull){
            holder.halfFullIb.setImageResource(R.mipmap.library_video_mediacontroller_to_half);
        }else{
            holder.halfFullIb.setImageResource(R.mipmap.library_video_mediacontroller_to_full);
        }
    }

    private void handleStartPlayVideo(VideoViewHolder holder){
        DebugTools.d("video2 handleStartPlayVideo mPlayState: " + mPlayState);
        if(mPlayState == PlayState.INIT){
            if(mVideoView == null){
                return;
            }

            if(TextUtils.isEmpty(mUrl)){
                if(mOnVideoControlListener != null){
                    mOnVideoControlListener.onError(0, "视频地址为空");
                }
                return;
            }

            if(mVideoView.getVisibility() != View.VISIBLE){
                mVideoView.setVisibility(View.VISIBLE);
            }

            initVideoView(holder, mVideoView);
            handlePrepareVideo(holder);
            setupVideoViewListener(mVideoView, holder);
            DebugTools.d("video2 handleStartPlayVideo setVideoPath-------------");
            mVideoView.setVideoPath(mUrl);
            if(mOnVideoControlListener != null){
                mOnVideoControlListener.onClickPlay();
            }
        }else if(mPlayState == PlayState.PAUSE){
            handlePlayVideo(holder);
            if(mOnVideoControlListener != null){
                mOnVideoControlListener.onClickResume();
            }
        }else if(mPlayState == PlayState.PLAYING){
            handlePauseVideo(holder);
            if(mOnVideoControlListener != null){
                mOnVideoControlListener.onClickPause();
            }
        }
    }


    private void handlePrepareVideo(final VideoViewHolder holder){
        showPreparePlayState(holder);
    }

    private void handlePlayVideo(final VideoViewHolder holder){
        showPlayingState(holder);;
        mVideoView.start();
    }

    private void handlePauseVideo(final VideoViewHolder holder){
        showPauseState(holder);
        mVideoView.pause();
    }

    public void pause(){
        if(mPlayState == PlayState.PLAYING){
            handlePauseVideo(mHolder);
            if(mOnVideoControlListener != null){
                mOnVideoControlListener.onClickPause();
            }
        }else if(mPlayState == PlayState.PREPARE){
            stop();
        }
    }

    public void stop(){
        if(!isInIdleState()){
            stopVideo(mHolder, mVideoView);
        }
    }

    private void stopVideo(VideoViewHolder holder, VideoView videoView){
        DebugTools.d("video2 stopVideo videoView null: " + (videoView == null));
        if(videoView != null){
            videoView.setVisibility(View.GONE);
            videoView.stopPlayback();
            holder.pb.setVisibility(View.GONE);
            showInitStateState(holder);
        }
    }

    private boolean isInIdleState(){
        return mPlayState == PlayState.INIT;
    }

    private PlayState mPlayState = PlayState.INIT;

    private enum PlayState{
        INIT, PREPARE, PLAYING, PAUSE
    }

    private void showPlayingState(VideoViewHolder holder){
        mPlayState = PlayState.PLAYING;

        updateViewVisibleState(holder.pb, View.GONE);
        updateViewVisibleState(holder.imgIv, View.INVISIBLE);
        updateViewVisibleState(holder.seekBar, View.VISIBLE);
        updateViewVisibleState(holder.playSymbolIv, View.INVISIBLE);

        holder.playPauseIb.setImageResource(R.mipmap.library_video_mediacontroller_pause);
        sendDismissVideoControlBarDelay(holder);
    }

    private void showPauseState(VideoViewHolder holder){
        mPlayState = PlayState.PAUSE;

        updateViewVisibleState(holder.pb, View.GONE);
        updateViewVisibleState(holder.imgIv, View.INVISIBLE);
        updateViewVisibleState(holder.seekBar, View.VISIBLE);
        updateViewVisibleState(holder.mediaControl, View.VISIBLE);
        updateViewVisibleState(holder.backIb, View.VISIBLE);
        updateViewVisibleState(holder.playSymbolIv, View.VISIBLE);

        holder.playPauseIb.setImageResource(R.mipmap.library_video_mediacontroller_play);
        mHandler.removeMessages(Constants.MSG_DISMISS_VIDEO_CONTROL_BAR);
    }

    private void showPreparePlayState(VideoViewHolder holder){
        mPlayState = PlayState.PREPARE;
        if(!mIsFullScreenMode){
            updateViewVisibleState(holder.imgIv, View.INVISIBLE);
        }

        holder.playPauseIb.setImageResource(R.mipmap.library_video_mediacontroller_pause);

        updateViewVisibleState(holder.pb, View.VISIBLE);
        updateViewVisibleState(holder.seekBar, View.VISIBLE);
        updateViewVisibleState(holder.mediaControl, View.INVISIBLE);
        updateViewVisibleState(holder.backIb, View.INVISIBLE);
        updateViewVisibleState(holder.playSymbolIv, View.INVISIBLE);
    }

    private void showInitStateState(VideoViewHolder holder){
        mPlayState = PlayState.INIT;
        holder.seekBar.setProgress(0);
        holder.playPauseIb.setImageResource(R.mipmap.library_video_mediacontroller_play);

        updateViewVisibleState(holder.pb, View.GONE);
        updateViewVisibleState(holder.imgIv, View.VISIBLE);
        updateViewVisibleState(holder.seekBar, View.INVISIBLE);
        updateViewVisibleState(holder.mediaControl, View.INVISIBLE);
        updateViewVisibleState(holder.backIb, View.INVISIBLE);
        updateViewVisibleState(holder.playSymbolIv, View.VISIBLE);

        mHandler.removeMessages(Constants.MSG_DISMISS_VIDEO_CONTROL_BAR);
    }

    private void updateViewVisibleState(View view, int visibleState){
        if(!mIsFullScreenMode && view == mHolder.backIb){
            return;
        }

        if(view == null){
            return;
        }

        if(view.getVisibility() != visibleState){
            view.setVisibility(visibleState);
        }
    }

    private void initVideoView(VideoViewHolder holder, VideoView videoView){
        videoView.setMediaController(holder.mediaControl); 
        holder.mediaControl.setMediaPlayer(videoView); 
        //让VideiView获取焦点 
        videoView.requestFocus();
    }

    private void setupVideoViewListener(final VideoView videoView, final VideoViewHolder holder){
        videoView.setOnCompletionListener(new OnCompletionListener() {
            @Override
            public void onCompletion(MediaPlayer mp) {
                DebugTools.d("video2 setOnPreparedListener onCompletion---, mPlayState: " + mPlayState);
                if(videoView != null){
                    if(mp != null){
                        mp.reset();
                    }
                    videoView.seekTo(0);
                    videoView.stopPlayback();
                    showInitStateState(holder);
                }
                if(mOnVideoControlListener != null){
                    mOnVideoControlListener.onCompletion();
                }
            }
        });
        videoView.setOnPreparedListener(new OnPreparedListener() {
            @Override
            public void onPrepared(MediaPlayer mp) {
//              mVideoView.setBackgroundColor(Color.TRANSPARENT);
                DebugTools.d("video2 setOnPreparedListener onPrepared, mPlayState: " + mPlayState);
//              if(!(mPlayState == PlayState.PREPARE || mPlayState == PlayState.INIT)){
//                  return;
//              }

                mVideoView.start();
                showPlayingState(holder);
                holder.mediaControl.show();
                updateViewVisibleState(holder.backIb, View.VISIBLE);
                mHandler.sendEmptyMessageDelayed(Constants.MSG_SET_VIDEO_VIEW_TRANSPARENT,
                        Constants.DELAY_MSG_SET_VIDEO_VIEW_TRANSPARENT);
                sendDismissVideoControlBarDelay(holder);
                DebugTools.d("video2 onPrepared mProgress: " + mProgress);
                if(mIsFullScreenMode && mProgress > 1000){
                    seekTo(mProgress);
                }
                if(mOnVideoControlListener != null){
                    mOnVideoControlListener.onPrepared();
                }
            }
        });


        ((WylVideoView)videoView).setOnGestureListener(new OnGestureListener() {
            @Override
            public void onSingleTapConfirmed() {
                boolean isShow = (holder.mediaControl.getVisibility() == View.VISIBLE);
                showVideoControlBar(holder, !isShow);
            }

            @Override
            public void onDoubleTap() {
                handleStartPlayVideo(holder);
            }
        });

        if(holder.videoRl != null){
            holder.videoRl.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    boolean isShow = (holder.mediaControl.getVisibility() == View.VISIBLE);
                    showVideoControlBar(holder, !isShow);
                }
            });
        }

        holder.mediaControl.setAdditionalSeekBarListener(new OnSeekBarChangeListener() {
            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
                DebugTools.d("video2 sbar2 onStopTrackingTouch");
                sendDismissVideoControlBarDelay(holder);
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {
                DebugTools.d("video2 sbar2 onStartTrackingTouch");
                mHandler.removeMessages(Constants.MSG_DISMISS_VIDEO_CONTROL_BAR);
            }

            @Override
            public void onProgressChanged(SeekBar seekBar, int progress,
                    boolean fromUser) {
                DebugTools.d("video2 sbar2 onProgressChanged");
            }
        });

        DebugTools.d("video2 setupVideoViewListener when click play video");
    }

    private void showVideoControlBar(final VideoViewHolder holder, final boolean isShow){
        mHandler.removeMessages(Constants.MSG_DISMISS_VIDEO_CONTROL_BAR);

        updateViewVisibleState(holder.mediaControl, View.VISIBLE);
        updateViewVisibleState(holder.backIb, View.VISIBLE);
        Animation anim = null;

        if(isShow){
            anim = new TranslateAnimation(0, 0, 100, 0);
        }else{
            anim = new TranslateAnimation(0, 0, 0, 100);
        }

        anim.setAnimationListener(new AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
            }

            @Override
            public void onAnimationRepeat(Animation animation) {
            }

            @Override
            public void onAnimationEnd(Animation animation) {
                if(isShow){
                    updateViewVisibleState(holder.mediaControl, View.VISIBLE);
                    updateViewVisibleState(holder.backIb, View.VISIBLE);
                    sendDismissVideoControlBarDelay(holder);
                }else{
                    updateViewVisibleState(holder.mediaControl, View.INVISIBLE);
                    updateViewVisibleState(holder.backIb, View.INVISIBLE);
                }
            }
        });
        anim.setDuration(300);
        holder.mediaControl.startAnimation(anim);

        if(holder.backIb != null){
            Animation backAnim = null;
            if(isShow){
                backAnim = new AlphaAnimation(0, 1);
            }else{
                backAnim = new AlphaAnimation(1, 0);
            }
            backAnim.setDuration(300);
            holder.backIb.startAnimation(backAnim);
        }
    }

    private void sendDismissVideoControlBarDelay(VideoViewHolder holder){
        Message msg = mHandler.obtainMessage(Constants.MSG_DISMISS_VIDEO_CONTROL_BAR, holder);
        mHandler.sendMessageDelayed(msg, Constants.DELAY_MSG_DISMISS_VIDEO_CONTROL_BAR);
    }

    private Handler mHandler = new Handler(){
        public void handleMessage(Message msg) {
            switch(msg.what){
            case Constants.MSG_SET_VIDEO_VIEW_TRANSPARENT:
                setVideoViewTranslate();
                break;

            case Constants.MSG_DISMISS_VIDEO_CONTROL_BAR:
                VideoViewHolder holder = (VideoViewHolder)msg.obj;
                showVideoControlBar(holder, false);
                break;
            }
        };
    };

    private void setVideoViewTranslate(){
        if(mVideoView != null){
            mVideoView.setBackgroundColor(Color.TRANSPARENT);
        }
    }

    private int mProgress;
    public void setProgress(int pos){
        mProgress = pos;
    }

    private void seekTo(int pos){
        if(mHolder != null && mHolder.mediaControl != null){
            mHolder.mediaControl.seekTo(pos);
        }
    }

    public static class VideoViewHolder{
        public WylMediaControl mediaControl;
//      public ImageView shareIv;
        public ImageView playPauseIb;
        public SeekBar seekBar;
        public ProgressBar pb;
        public ImageView imgIv;
        public ImageView playSymbolIv;
        public RelativeLayout videoRl;
        public ImageButton halfFullIb;
        public ImageButton backIb;
        public TextView totalTimeTv;

        public VideoViewHolder(View convertView){
            mediaControl = (WylMediaControl) convertView.findViewById(R.id.media_control);
//          shareIv = (ImageView) convertView.findViewById(R.id.mediacontroller_share_iv);
            playPauseIb = (ImageView) convertView.findViewById(R.id.mediacontroller_play_pause);
            seekBar = (SeekBar) convertView.findViewById(R.id.mediacontroller_seekbar);
            pb = (ProgressBar) convertView.findViewById(R.id.loading_pb);
            imgIv = (ImageView) convertView.findViewById(R.id.video_image_iv);

            playSymbolIv = (ImageView) convertView.findViewById(R.id.play_symbol_iv);
            videoRl = (RelativeLayout) convertView.findViewById(R.id.video_rl);
            halfFullIb = (ImageButton) convertView.findViewById(R.id.mediacontroller_half_full_ib);
//          backIb = (ImageButton) convertView.findViewById(R.id.video_back_iv);
            totalTimeTv = (TextView) convertView.findViewById(R.id.mediacontroller_time_total);
        }
    }

    private OnVideoControlListener mOnVideoControlListener;
    public void setOnVideoControlListener(OnVideoControlListener listener){
        mOnVideoControlListener = listener;
    }
    public interface OnVideoControlListener{
        public void onClickPlay();
        public void onPrepared();
        public void onClickResume();
        public void onClickPause();
        public void onCompletion();
        public void onClickHalfFullScreen();
        public void onError(int code, String msg);
    }

    public static class OnVideoControlProxy implements OnVideoControlListener{
        @Override
        public void onClickPlay() {
        }
        @Override
        public void onPrepared() {
        }
        @Override
        public void onClickResume() {
        }
        @Override
        public void onClickPause() {
        }
        @Override
        public void onCompletion() {
        }
        @Override
        public void onClickHalfFullScreen() {
        }
        @Override
        public void onError(int code, String msg) {
        }
    }
}

WylMediaControl,重写了下系统的MediaController,方便自定义

public class WylMediaControl extends MediaController {
    private MediaPlayerControl mPlayer;
    private Context mContext;
    private View mAnchor;
    private View mRoot;
    private WindowManager mWindowManager;
    private Window mWindow;
    private View mDecor;
    private WindowManager.LayoutParams mDecorLayoutParams;
    private ProgressBar mProgress;
    private TextView mEndTime, mCurrentTime;
    private boolean mShowing;
    private boolean mDragging;
    private static final int sDefaultTimeout = 3000;
    private static final int FADE_OUT = 1;
    private static final int SHOW_PROGRESS = 2;
    private boolean mUseFastForward;
    private boolean mFromXml;
    private boolean mListenersSet;
    private OnClickListener mNextListener, mPrevListener;
    StringBuilder mFormatBuilder;
    Formatter mFormatter;
    private ImageView mPauseButton;
//  private ImageButton mPauseButton;
    private ImageButton mFfwdButton;
    private ImageButton mRewButton;
    private ImageButton mNextButton;
    private ImageButton mPrevButton;
    private ImageView mShareBtn;

    public WylMediaControl(Context context, AttributeSet attrs) {
        super(context, attrs);
        mRoot = this;
        mContext = context;
        mUseFastForward = true;
        mFromXml = true;
    }

    @Override
    public void onFinishInflate() {
        if (mRoot != null)
            initControllerView(mRoot);
    }

    public WylMediaControl(Context context, boolean useFastForward) {
        super(context);
        mContext = context;
        mUseFastForward = useFastForward;
        initFloatingWindowLayout();
        // initFloatingWindow();
    }

    public WylMediaControl(Context context) {
        this(context, true);
    }

    // private void initFloatingWindow() {
    // mWindowManager =
    // (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
    // mWindow = PolicyManager.makeNewWindow(mContext);
    // mWindow.setWindowManager(mWindowManager, null, null);
    // mWindow.requestFeature(Window.FEATURE_NO_TITLE);
    // mDecor = mWindow.getDecorView();
    // mDecor.setOnTouchListener(mTouchListener);
    // mWindow.setContentView(this);
    // mWindow.setBackgroundDrawableResource(android.R.color.transparent);
    //
    // // While the media controller is up, the volume control keys should
    // // affect the media stream type
    // mWindow.setVolumeControlStream(AudioManager.STREAM_MUSIC);
    //
    // setFocusable(true);
    // setFocusableInTouchMode(true);
    // setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
    // requestFocus();
    // }

    // Allocate and initialize the static parts of mDecorLayoutParams. Must
    // also call updateFloatingWindowLayout() to fill in the dynamic parts
    // (y and width) before mDecorLayoutParams can be used.
    private void initFloatingWindowLayout() {
        mDecorLayoutParams = new WindowManager.LayoutParams();
        WindowManager.LayoutParams p = mDecorLayoutParams;
        p.gravity = Gravity.TOP | Gravity.LEFT;
        p.height = LayoutParams.WRAP_CONTENT;
        p.x = 0;
        p.format = PixelFormat.TRANSLUCENT;
        p.type = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
        p.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
                | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
        p.token = null;
        p.windowAnimations = 0; // android.R.style.DropDownAnimationDown;
    }

    // Update the dynamic parts of mDecorLayoutParams
    // Must be called with mAnchor != NULL.
    private void updateFloatingWindowLayout() {
        int[] anchorPos = new int[2];
        mAnchor.getLocationOnScreen(anchorPos);

        // we need to know the size of the controller so we can properly
        // position it
        // within its space
        mDecor.measure(MeasureSpec.makeMeasureSpec(mAnchor.getWidth(),
                MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(
                mAnchor.getHeight(), MeasureSpec.AT_MOST));

        WindowManager.LayoutParams p = mDecorLayoutParams;
        p.width = mAnchor.getWidth();
        p.x = anchorPos[0] + (mAnchor.getWidth() - p.width) / 2;
        p.y = anchorPos[1] + mAnchor.getHeight() - mDecor.getMeasuredHeight();
    }

    // This is called whenever mAnchor's layout bound changes
    private OnLayoutChangeListener mLayoutChangeListener = new OnLayoutChangeListener() {
        public void onLayoutChange(View v, int left, int top, int right,
                int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
            updateFloatingWindowLayout();
            if (mShowing) {
                mWindowManager.updateViewLayout(mDecor, mDecorLayoutParams);
            }
        }
    };

    private OnTouchListener mTouchListener = new OnTouchListener() {
        public boolean onTouch(View v, MotionEvent event) {
            if (event.getAction() == MotionEvent.ACTION_DOWN) {
                if (mShowing) {
                    hide();
                }
            }
            return false;
        }
    };

    @Override
    public void setMediaPlayer(MediaPlayerControl player) {
        mPlayer = player;
        updatePausePlay();
    }

    /**
     * Set the view that acts as the anchor for the control view. This can for
     * example be a VideoView, or your Activity's main view. When VideoView
     * calls this method, it will use the VideoView's parent as the anchor.
     * 
     * @param view
     *            The view to which to anchor the controller when it is visible.
     */
    public void setAnchorView(View view) {
//      if (mAnchor != null) {
//          mAnchor.removeOnLayoutChangeListener(mLayoutChangeListener);
//      }
        mAnchor = view;
//      if (mAnchor != null) {
//          mAnchor.addOnLayoutChangeListener(mLayoutChangeListener);
//      }

        if (!mFromXml) {
            // FrameLayout.LayoutParams frameParams = new
            // FrameLayout.LayoutParams(
            // ViewGroup.LayoutParams.MATCH_PARENT,
            // ViewGroup.LayoutParams.MATCH_PARENT
            // );
            //
            // removeAllViews();
            // View v = makeControllerView();
            // addView(v, frameParams);
        }

    }

    private MediaControlListener mMediaControlListener;

    public interface MediaControlListener{
        public void onClickStart();
        public void onClickPause();
    }

    /**
     * Create the view that holds the widgets that control playback. Derived
     * classes can override this to create their own.
     * 
     * @return The controller view.
     * @hide This doesn't work as advertised
     */
    // protected View makeControllerView() {
    // LayoutInflater inflate = (LayoutInflater)
    // mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    // mRoot = inflate.inflate(com.android.internal.R.layout.media_controller,
    // null);
    //
    // initControllerView(mRoot);
    //
    // return mRoot;
    // }

    private void initControllerView(View v) {
        mPauseButton = (ImageView) v.findViewById(R.id.mediacontroller_play_pause);
//      if (mPauseButton != null) {
//          mPauseButton.requestFocus();
//          mPauseButton.setOnClickListener(mPauseListener);
//      }

        if (mShareBtn != null) {
//          mShareBtn.setOnClickListener(mShareListener);
        }



//      mFfwdButton = (ImageButton) v.findViewById(com.android.internal.R.id.ffwd);
//      if (mFfwdButton != null) {
//          mFfwdButton.setOnClickListener(mFfwdListener);
//          if (!mFromXml) {
//              mFfwdButton.setVisibility(mUseFastForward ? View.VISIBLE
//                      : View.GONE);
//          }
//      }
//
//      mRewButton = (ImageButton) v.findViewById(com.android.internal.R.id.rew);
//      if (mRewButton != null) {
//          mRewButton.setOnClickListener(mRewListener);
//          if (!mFromXml) {
//              mRewButton.setVisibility(mUseFastForward ? View.VISIBLE
//                      : View.GONE);
//          }
//      }

        // By default these are hidden. They will be enabled when
        // setPrevNextListeners() is called
//      mNextButton = (ImageButton) v.findViewById(com.android.internal.R.id.next);
//      if (mNextButton != null && !mFromXml && !mListenersSet) {
//          mNextButton.setVisibility(View.GONE);
//      }
//      mPrevButton = (ImageButton) v.findViewById(com.android.internal.R.id.prev);
//      if (mPrevButton != null && !mFromXml && !mListenersSet) {
//          mPrevButton.setVisibility(View.GONE);
//      }

        mProgress = (ProgressBar) v.findViewById(R.id.mediacontroller_seekbar);
        if (mProgress != null) {
            if (mProgress instanceof SeekBar) {
                SeekBar seeker = (SeekBar) mProgress;
                seeker.setOnSeekBarChangeListener(mSeekListener);
            }
            mProgress.setMax(1000);
        }

        mEndTime = (TextView) v.findViewById(R.id.mediacontroller_time_total);
        mCurrentTime = (TextView) v.findViewById(R.id.mediacontroller_time_current);
        mFormatBuilder = new StringBuilder();
        mFormatter = new Formatter(mFormatBuilder, Locale.getDefault());

        installPrevNextListeners();
    }

    /**
     * Show the controller on screen. It will go away automatically after 3
     * seconds of inactivity.
     */
    public void show() {
        show(sDefaultTimeout);
    }

    /**
     * Disable pause or seek buttons if the stream cannot be paused or seeked.
     * This requires the control interface to be a MediaPlayerControlExt
     */
    private void disableUnsupportedButtons() {
        try {
            if (mPauseButton != null && !mPlayer.canPause()) {
                mPauseButton.setEnabled(false);
            }
            if (mRewButton != null && !mPlayer.canSeekBackward()) {
                mRewButton.setEnabled(false);
            }
            if (mFfwdButton != null && !mPlayer.canSeekForward()) {
                mFfwdButton.setEnabled(false);
            }
        } catch (IncompatibleClassChangeError ex) {
            // We were given an old version of the interface, that doesn't have
            // the canPause/canSeekXYZ methods. This is OK, it just means we
            // assume the media can be paused and seeked, and so we don't
            // disable
            // the buttons.
        }
    }

    /**
     * Show the controller on screen. It will go away automatically after
     * 'timeout' milliseconds of inactivity.
     * 
     * @param timeout
     *            The timeout in milliseconds. Use 0 to show the controller
     *            until hide() is called.
     */
    public void show(int timeout) {
        if (!mShowing && mAnchor != null) {

            if (mFromXml) {
                setVisibility(View.VISIBLE);
//              return;
            }

            setProgress();
            if (mPauseButton != null) {
                mPauseButton.requestFocus();
            }
            disableUnsupportedButtons();
//          updateFloatingWindowLayout();
//          mWindowManager.addView(mDecor, mDecorLayoutParams);
            mShowing = true;
        }
        updatePausePlay();

        // cause the progress bar to be updated even if mShowing
        // was already true. This happens, for example, if we're
        // paused with the progress bar showing the user hits play.
        mHandler.sendEmptyMessage(SHOW_PROGRESS);

        DebugTools.d("control2 show");

        Message msg = mHandler.obtainMessage(FADE_OUT);
        if (timeout != 0) {
            mHandler.removeMessages(FADE_OUT);
            mHandler.sendMessageDelayed(msg, timeout);
        }
    }

    public boolean isShowing() {
        return mShowing;
    }

    /**
     * Remove the controller from the screen.
     */
    public void hide() {
//      if (mAnchor == null)
//          return;
//
//      if (mShowing) {
//          try {
//              mHandler.removeMessages(SHOW_PROGRESS);
//              mWindowManager.removeView(mDecor);
//          } catch (IllegalArgumentException ex) {
//              Log.w("MediaController", "already removed");
//          }
//          mShowing = false;
//      }
    }

    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            int pos;
            switch (msg.what) {
            case FADE_OUT:
//              hide();
                break;
            case SHOW_PROGRESS:
                pos = setProgress();
                if (!mDragging && mShowing && mPlayer.isPlaying()) {
                    msg = obtainMessage(SHOW_PROGRESS);
                    sendMessageDelayed(msg, 1000 - (pos % 1000));
                }
                break;
            }
        }
    };

    private String stringForTime(int timeMs) {
        int totalSeconds = timeMs / 1000;

        int seconds = totalSeconds % 60;
        int minutes = (totalSeconds / 60) % 60;
        int hours = totalSeconds / 3600;

        mFormatBuilder.setLength(0);
        if (hours > 0) {
            return mFormatter.format("%d:%02d:%02d", hours, minutes, seconds)
                    .toString();
        } else {
            return mFormatter.format("%02d:%02d", minutes, seconds).toString();
        }
    }

    private int setProgress() {
        DebugTools.d("control2 setProgress");

        if (mPlayer == null || mDragging) {
            return 0;
        }
        int position = mPlayer.getCurrentPosition();
        int duration = mPlayer.getDuration();

        DebugTools.d("control2 setProgress position: " + position + " duration: " + duration);

        if (mProgress != null) {
            if (duration > 0) {
                // use long to avoid overflow
                long pos = 1000L * position / duration;
                mProgress.setProgress((int) pos);
            }
            int percent = mPlayer.getBufferPercentage();
            mProgress.setSecondaryProgress(percent * 10);
        }

        if (mEndTime != null)
            mEndTime.setText(stringForTime(duration));
        if (mCurrentTime != null)
            mCurrentTime.setText(stringForTime(position));

        return position;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        show(sDefaultTimeout);
        return true;
    }

    @Override
    public boolean onTrackballEvent(MotionEvent ev) {
        show(sDefaultTimeout);
        return false;
    }

    @Override
    public boolean dispatchKeyEvent(KeyEvent event) {
        int keyCode = event.getKeyCode();
        final boolean uniqueDown = event.getRepeatCount() == 0
                && event.getAction() == KeyEvent.ACTION_DOWN;
        if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK
                || keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE
                || keyCode == KeyEvent.KEYCODE_SPACE) {
            if (uniqueDown) {
                doPauseResume();
                show(sDefaultTimeout);
                if (mPauseButton != null) {
                    mPauseButton.requestFocus();
                }
            }
            return true;
        } else if (keyCode == KeyEvent.KEYCODE_MEDIA_PLAY) {
            if (uniqueDown && !mPlayer.isPlaying()) {
                mPlayer.start();
                updatePausePlay();
                show(sDefaultTimeout);
            }
            return true;
        } else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP
                || keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE) {
            if (uniqueDown && mPlayer.isPlaying()) {
                mPlayer.pause();
                updatePausePlay();
                show(sDefaultTimeout);
            }
            return true;
        } else if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
                || keyCode == KeyEvent.KEYCODE_VOLUME_UP
                || keyCode == KeyEvent.KEYCODE_VOLUME_MUTE
                || keyCode == KeyEvent.KEYCODE_CAMERA) {
            // don't show the controls for volume adjustment
            return super.dispatchKeyEvent(event);
        } else if (keyCode == KeyEvent.KEYCODE_BACK
                || keyCode == KeyEvent.KEYCODE_MENU) {
            if (uniqueDown) {
                hide();
            }
            return true;
        }

        show(sDefaultTimeout);
        return super.dispatchKeyEvent(event);
    }

    private OnClickListener mPauseListener = new OnClickListener() {
        public void onClick(View v) {
            doPauseResume();
            show(sDefaultTimeout);
        }
    };

    private OnClickListener mShareListener = new OnClickListener() {
        public void onClick(View v) {
            if(mMediaControlListener != null){
//              mMediaControlListener.onClickShare(v);
            }
        }
    };



    public void setMediaControlListener(MediaControlListener listener){
        mMediaControlListener = listener;
    }

    private void updatePausePlay() {
        if (mRoot == null || mPauseButton == null)
            return;

//      if (mPlayer.isPlaying()) {
//          mPauseButton.setImageResource(R.drawable.mediacontroller_pause);
            mPauseButton.setImageResource(com.android.internal.R.drawable.ic_media_pause);
//      } else {
//          mPauseButton.setImageResource(R.drawable.mediacontroller_play);
            mPauseButton.setImageResource(com.android.internal.R.drawable.ic_media_play);
//      }
    }

    public void doPause(){
        if (mPlayer.isPlaying()) {
            mPlayer.pause();
            mPauseButton.setImageResource(R.mipmap.library_video_mediacontroller_play);
            if(mMediaControlListener != null){
                mMediaControlListener.onClickPause();
            }
        }
    }

    public void doPauseResume() {
        if (mPlayer.isPlaying()) {
            mPlayer.pause();
            mPauseButton.setImageResource(R.mipmap.library_video_mediacontroller_play);
            if(mMediaControlListener != null){
                mMediaControlListener.onClickPause();
            }
        } else {
            mPlayer.start();
            mPauseButton.setImageResource(R.mipmap.library_video_mediacontroller_pause);
            if(mMediaControlListener != null){
                mMediaControlListener.onClickStart();
            }
        }
//      updatePausePlay();
    }

    // There are two scenarios that can trigger the seekbar listener to trigger:
    //
    // The first is the user using the touchpad to adjust the posititon of the
    // seekbar's thumb. In this case onStartTrackingTouch is called followed by
    // a number of onProgressChanged notifications, concluded by
    // onStopTrackingTouch.
    // We're setting the field "mDragging" to true for the duration of the
    // dragging
    // session to avoid jumps in the position in case of ongoing playback.
    //
    // The second scenario involves the user operating the scroll ball, in this
    // case there WON'T BE onStartTrackingTouch/onStopTrackingTouch
    // notifications,
    // we will simply apply the updated position without suspending regular
    // updates.
    private OnSeekBarChangeListener mSeekListener = new OnSeekBarChangeListener() {
        public void onStartTrackingTouch(SeekBar bar) {
            show(3600000);

            mDragging = true;

            // By removing these pending progress messages we make sure
            // that a) we won't update the progress while the user adjusts
            // the seekbar and b) once the user is done dragging the thumb
            // we will post one of these messages to the queue again and
            // this ensures that there will be exactly one message queued up.
            mHandler.removeMessages(SHOW_PROGRESS);

            if(mAdditionalSeekListener != null){
                mAdditionalSeekListener.onStartTrackingTouch(bar);
            }
        }

        public void onProgressChanged(SeekBar bar, int progress,
                boolean fromuser) {
            if (!fromuser) {
                // We're not interested in programmatically generated changes to
                // the progress bar's position.
                return;
            }

            long duration = mPlayer.getDuration();
            long newposition = (duration * progress) / 1000L;
            mPlayer.seekTo((int) newposition);
            if (mCurrentTime != null)
                mCurrentTime.setText(stringForTime((int) newposition));

            if(mAdditionalSeekListener != null){
                mAdditionalSeekListener.onProgressChanged(bar, progress, fromuser);
            }
        }

        public void onStopTrackingTouch(SeekBar bar) {
            mDragging = false;
            setProgress();
            updatePausePlay();
            show(sDefaultTimeout);

            // Ensure that progress is properly updated in the future,
            // the call to show() does not guarantee this because it is a
            // no-op if we are already showing.
            mHandler.sendEmptyMessage(SHOW_PROGRESS);

            if(mAdditionalSeekListener != null){
                mAdditionalSeekListener.onStopTrackingTouch(bar);
            }
        }
    };

    private OnSeekBarChangeListener mAdditionalSeekListener;
    public void setAdditionalSeekBarListener(OnSeekBarChangeListener listener){
        mAdditionalSeekListener = listener;
    }

    @Override
    public void setEnabled(boolean enabled) {
        if (mPauseButton != null) {
            mPauseButton.setEnabled(enabled);
        }
        if (mFfwdButton != null) {
            mFfwdButton.setEnabled(enabled);
        }
        if (mRewButton != null) {
            mRewButton.setEnabled(enabled);
        }
        if (mNextButton != null) {
            mNextButton.setEnabled(enabled && mNextListener != null);
        }
        if (mPrevButton != null) {
            mPrevButton.setEnabled(enabled && mPrevListener != null);
        }
        if (mProgress != null) {
            mProgress.setEnabled(enabled);
        }
        disableUnsupportedButtons();
        super.setEnabled(enabled);
    }

    @Override
    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
        super.onInitializeAccessibilityEvent(event);
        event.setClassName(MediaController.class.getName());
    }

    @Override
    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
        super.onInitializeAccessibilityNodeInfo(info);
        info.setClassName(MediaController.class.getName());
    }

    private OnClickListener mRewListener = new OnClickListener() {
        public void onClick(View v) {
            int pos = mPlayer.getCurrentPosition();
            pos -= 5000; // milliseconds
            mPlayer.seekTo(pos);
            setProgress();

            show(sDefaultTimeout);
        }
    };

    private OnClickListener mFfwdListener = new OnClickListener() {
        public void onClick(View v) {
            int pos = mPlayer.getCurrentPosition();
            pos += 15000; // milliseconds
            mPlayer.seekTo(pos);
            setProgress();

            show(sDefaultTimeout);
        }
    };

    public void seekTo(int pos){
        mPlayer.seekTo(pos);
        setProgress();

        show(sDefaultTimeout);
    }

    private void installPrevNextListeners() {
        if (mNextButton != null) {
            mNextButton.setOnClickListener(mNextListener);
            mNextButton.setEnabled(mNextListener != null);
        }

        if (mPrevButton != null) {
            mPrevButton.setOnClickListener(mPrevListener);
            mPrevButton.setEnabled(mPrevListener != null);
        }
    }

    public void setPrevNextListeners(OnClickListener next,
            OnClickListener prev) {
        mNextListener = next;
        mPrevListener = prev;
        mListenersSet = true;

        if (mRoot != null) {
            installPrevNextListeners();

            if (mNextButton != null && !mFromXml) {
                mNextButton.setVisibility(View.VISIBLE);
            }
            if (mPrevButton != null && !mFromXml) {
                mPrevButton.setVisibility(View.VISIBLE);
            }
        }
    }
}

源码地址:http://download.csdn.net/detail/paddyy/9414789

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值