MP3Play项目实战 (1)

1.

我们用到了自定义viewgroup.

实现左中右三个页面的左右滑动,类似于viewpager的滑动

//自定义viewgroup
public class DragableLuncher extends ViewGroup {
    //按钮背景颜色
    int choseColor , defaultColor;
    //底部按钮数组
    ImageButton[] bottomBar;
    //滚动对象
    private Scroller mScroller;
    //触摸功能类 触摸速度
    private VelocityTracker mVelocityTracker;
    //滚动起始坐标
    private int mScrollX = 0;
    //显示第几view
    private int mCurrentScreen = 0;
    //滚动结束X坐标
    private float mLastMotionX;

    private static final int SNAP_VELOCITY = 1000;
    private final static int TOUCH_STATE_REST = 0;
    private final static int TOUCH_STATE_SCROLLING = 1;

    private int mTouchState = TOUCH_STATE_REST;
    //用户最小滑动距离
    private int mTouchSlop = 0;

    public DragableLuncher(Context context) {
        super(context);
        mScroller = new Scroller(context);
        //初始化最小滑动距离
        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
        this.setLayoutParams(new ViewGroup.LayoutParams(LayoutParams.WRAP_CONTENT , LayoutParams.FILL_PARENT));
    }

    public DragableLuncher(Context context, AttributeSet attrs) {
        super(context, attrs);
        mScroller = new Scroller(context);
        //初始化最小滑动距离
        mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
        this.setLayoutParams(new ViewGroup.LayoutParams(LayoutParams.WRAP_CONTENT , LayoutParams.FILL_PARENT));
        //xml中 默认显示第几个view在屏幕中
        TypedArray a = context.obtainStyledAttributes(attrs , R.styleable.DragableLuncher);
        mCurrentScreen = a.getInteger(R.styleable.DragableLuncher_default_screen , 0);
        a.recycle();
    }

    //touch拦截事件
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        final int action = ev.getAction();
        if ((action == MotionEvent.ACTION_MOVE) && (mTouchState != TOUCH_STATE_REST)){
            return true;
        }
        final float x = ev.getX();
        switch (action){
            case MotionEvent.ACTION_DOWN:
                mLastMotionX = x;
                mTouchState = mScroller.isFinished()?TOUCH_STATE_REST:TOUCH_STATE_SCROLLING;
                break;
            case MotionEvent.ACTION_MOVE:
                final int xDiff = (int) Math.abs(x-mLastMotionX);
                boolean xMoved = xDiff > mTouchSlop;
                if (xMoved){
                    mTouchState = TOUCH_STATE_SCROLLING;
                }
                break;
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
                mTouchState = TOUCH_STATE_REST;
                break;
        }

        return super.onInterceptTouchEvent(ev);
    }

    //触摸滑动打开
    public boolean isOpen = true;
    public boolean isOpenTouchAnima(boolean b){
        isOpen = b;
        return isOpen;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (isOpen){
            if (mVelocityTracker == null){
                mVelocityTracker = VelocityTracker.obtain();
            }
            //将触摸事件添加到速度探测器中
            mVelocityTracker.addMovement(event);
            final float x = event.getX();
            final int action = event.getAction();
            switch (action) {
                case MotionEvent.ACTION_DOWN:
                    if (!mScroller.isFinished()){
                        mScroller.abortAnimation();
                    }
                    mLastMotionX = x;
                    break;
                case MotionEvent.ACTION_MOVE:
                    final int deltaX = (int) (mLastMotionX - x);
                    mLastMotionX = x;
                    if (deltaX < 0){
                        //如果mscrollx小于等于0.这不要向右滑动,界面往左了,因为已经在最左边了.
                        if (mScrollX > 0){
                            scrollBy(Math.max(-mScrollX , deltaX) , 0);
                        }
                    }
                    else if (deltaX > 0){
                        //向左滑,界面往右,取得最大滑动距离
                        final int availableToScroll = getChildAt(getChildCount() - 1).getRight() - mScrollX - getWidth();
                        if (availableToScroll > 0){
                            scrollBy(Math.min(availableToScroll , deltaX) , 0);
                        }
                    }
                    break;
                case MotionEvent.ACTION_UP:
                    final VelocityTracker velocityTracker = mVelocityTracker;
                    //你想要的单位速度。1的值提供每毫秒像素,1000提供每秒像素等
                    velocityTracker.computeCurrentVelocity(1000);
                    //得到x方向上的速度
                    int velocityX = (int) velocityTracker.getXVelocity();
                    //左右滑动有速度的话界面要很快的滑动
                    if (velocityX > SNAP_VELOCITY && mCurrentScreen > 0){
                        //滑到左边界面
                        snapToScreen(mCurrentScreen - 1);
                    }
                    else if (velocityX < -SNAP_VELOCITY && mCurrentScreen < getChildCount() - 1){
                        //滑到右边界面
                        snapToScreen(mCurrentScreen + 1);
                    }
                    else {
                        snapToDesitination();
                    }
                    if (mVelocityTracker != null){
                        mVelocityTracker.recycle();
                        mVelocityTracker = null;
                    }
                    mTouchState = TOUCH_STATE_REST;
                    break;
                case MotionEvent.ACTION_CANCEL:
                    mTouchState = TOUCH_STATE_REST;
            }
            mScrollX = this.getScrollX();
        }
        else {
            return false;
        }
        if (bottomBar != null){
            for (int k = 0 ; k < bottomBar.length ; k++){
                if (k == mCurrentScreen){
                    bottomBar[k].setBackgroundColor(choseColor);
                } else {
                    bottomBar[k].setBackgroundColor(defaultColor);
                }
            }
        }
        return true;
    }

    public void setBottomBarBg(ImageButton[] ib , int choseColor , int defaultColor){
        this.bottomBar = ib;
        this.choseColor = choseColor;
        this.defaultColor = defaultColor;
    }

    public void snapToDesitination(){
        final int screenWidth = getWidth();
        //mSrcrolx表示滑动的距离,向右的为正,向左滑为负
        final int whichScreen = (mScrollX + (screenWidth/2))/screenWidth;
        snapToScreen(whichScreen);
    }

    //带动画跳转界面
    public void snapToScreen(int whichScreen) {
        mCurrentScreen = whichScreen;
        final int newX = whichScreen*getWidth();
        //当手离开屏幕的一瞬间,view要滑到上一页或下一页,但并不是从这页的左边开始滑动,还要计算在move
        //过程中已经滑动的距离,从这个位置开始滑动到上一页或下一页
        final int deltaX = newX - mScrollX;
        mScroller.startScroll(mScrollX , 0 , deltaX , 0 , Math.abs(deltaX)*2 );
        invalidate();
    }

    //不带动画跳转界面
    public void setToScreen(int whichScreen){
        mCurrentScreen = whichScreen;
        final int newX = whichScreen*getWidth();
        mScroller.startScroll(newX , 0 , 0 , 0 , 10);
        //不带动画的显示界面
        invalidate();
    }

    //当前是第几屏幕view
    public int getCurrentScreen(){
        return mCurrentScreen;
    }

    //主界面布局
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int childLeft = 0;
        final int count = getChildCount();
        for (int i = 0 ; i< count ; i++){
            final View child = getChildAt(i);
            if (child.getVisibility() != View.GONE){
                final int childWidth = child.getMeasuredWidth();
                child.layout(childLeft , 0 , childLeft + childWidth , child.getMeasuredHeight());
                childLeft += childWidth;
            }
        }
    }

    @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("error mode");
        }
        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        if (heightMode != MeasureSpec.EXACTLY){
            throw new IllegalStateException("error mode");
        }
        final int count = getChildCount();
        for (int i = 0 ; i < count ; i++){
            getChildAt(i).measure(widthMeasureSpec , heightMeasureSpec);
        }
        //滚动到指定的view
        scrollTo(mCurrentScreen*width , 0);
    }

    //1.真正让ViewGroup滑动的是scrollTo,scrollBy。
    // computeScroll的作用是计算ViewGroup如何滑动。而computeScroll是通过draw来调用的
    //2.computeScroll和Scroller要是飞得拉关系的话,那就是computeScroll可以参考Scroller计算结果来影响scrollTo,scrollBy,从而使得滑动发生改变。
    // 也就是Scroller不会调用computeScroll,反而是computeScroll调用Scroller。
    //3.如上所说computeScroll调用Scroller,只要computeScroll调用连续,Scroller也会连续,
    // 实质上computeScroll的连续性又invalidate方法控制,scrollTo,scrollBy都会调用invalidate,
    // 而invalidate回去触发draw,从而computeScroll被连续调用,
    // 综上,Scroller也会被连续调用,除非invalidate停止调用。
    //4.computeScroll参考Scroller影响scrollTo,scrollBy,实质上,为了不重复影响scrollTo,scrollBy,
    // 那么Scroller必须终止计算currX,currY。要知道计算有没有终止,
    // 需要通过mScroller.computeScrollOffset()
    //5.https://www.linuxidc.com/Linux/2016-01/127276.htm [[理解scroll]]
    @Override
    public void computeScroll() {
        if (mScroller.computeScrollOffset()){
            mScrollX = mScroller.getCurrX();
            scrollTo(mScrollX , 0);
            postInvalidate();
        }
    }
    
    
}

2.

也是一个自定义viewgroup 和上面那个的作用类似.

public class BigDragableLuncher extends ViewGroup {
    int choseColor , defaultColor;
    //底部按钮数组
    ImageButton[] bottombar;
    private Scroller mScroller;
    //滚动起始x坐标
    private int mScrollX = 0;
    //显示第几屏幕的view
    private int mCurrentScreen = 0;
    public int mTouchSlop = 0;


    public BigDragableLuncher(Context context) {
        super(context);
        mScroller = new Scroller(context);
        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
        this.setLayoutParams(new ViewGroup.LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.FILL_PARENT));
    }

    public BigDragableLuncher(Context context, AttributeSet attrs) {
        super(context, attrs);
        mScroller = new Scroller(context);
        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
        this.setLayoutParams(new ViewGroup.LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.FILL_PARENT));
        //获取属性文件
        TypedArray a = getContext().obtainStyledAttributes(attrs , R.styleable.DragableLuncher);
        //获取具体属性
        mCurrentScreen = a.getInteger(R.styleable.DragableLuncher_default_screen , 0);
        a.recycle();
    }

    public void setBottombarBg(ImageButton[] lb , int choseColor , int defaultColor){
        this.bottombar = lb;
        this.choseColor = choseColor;
        this.defaultColor = defaultColor;
    }

    public void snapToDestination(){
        final int screenWidth = getWidth();
        //滑动超过1/2屏幕,进入下一个view
        final int whichScreen = (mScrollX + (screenWidth/2))/screenWidth;
        snapToScreen(whichScreen);
    }

    //屏幕滑动
    private void snapToScreen(int whichScreen) {
        mCurrentScreen = whichScreen;
        final int newX = whichScreen*getWidth();
        final int detal = newX -mScrollX;
        //主要代码
        mScroller.startScroll(mScrollX , 0 , detal , 0 , Math.abs(detal)*2);
        invalidate();
    }

    public void setToscreen(int whichScreen){
        mCurrentScreen = whichScreen;
        final int newX = whichScreen*getWidth();
        //主要代码,无动画显示界面
        mScroller.startScroll(newX , 0 , 0 , 0 , 10);
        invalidate();
    }

    public int getCurrentScreen(){
        return mCurrentScreen;
    }


    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int childLeft = 0;
        final int count = getChildCount();
        for (int i = 0 ; i < count; i++){
            final View child = getChildAt(i);
            if (child.getVisibility() != View.GONE){
                final int childWidth = child.getMeasuredWidth();
                child.layout(childLeft , 0 , childLeft + childWidth , child.getMeasuredHeight());
                childLeft += childWidth;
            }
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        final int width = MeasureSpec.getSize(widthMeasureSpec);
        final int height = MeasureSpec.getSize(heightMeasureSpec);
        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        if (widthMode != MeasureSpec.EXACTLY){
            throw new IllegalStateException("error mode");
        }
        if (heightMode != MeasureSpec.EXACTLY){
            throw new IllegalStateException("error mode");
        }
        //将宽高信息传递给子元素
        final int count = getChildCount();
        for (int i = 0 ; i < count ; i++){
            getChildAt(i).measure(widthMeasureSpec , heightMeasureSpec);
        }
        scrollTo(mCurrentScreen*width , 0);
    }

    @Override
    public void computeScroll() {
        if (mScroller.computeScrollOffset()){
            mScrollX = mScroller.getCurrX();
            scrollTo(mScrollX , 0);
            postInvalidate();
        }
    }

}

3.

对于上面那个view,要改动一个地方

//获取属性文件
//在布局中使用自定义属性时发现并不会自动提示声明好的属性,
// 一直困惑了很久才发现自定义属性的名称必须和自定义view的类名一样才会有提示
TypedArray a = context.obtainStyledAttributes(attrs , R.styleable.BigDragableLuncher);
//获取具体属性
mCurrentScreen = a.getInteger(R.styleable.BigDragableLuncher_default_screen , 0);
a.recycle();

主界面结构布局


<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:liuyan="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="@drawable/musicplayer_bkg"
    android:gravity="center"
    android:layout_gravity="top"
    >
    <com.example.liuyan.mp3play.customview.BigDragableLuncher
        liuyan:default_screen="1"
        android:id="@+id/all_space"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent">

        <include android:id="@+id/music_list" layout="@layout/music_list"/>

        <RelativeLayout
            android:id="@+id/linearlayout1"
            android:orientation="vertical"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            >
            <RelativeLayout
                android:id="@+id/linearlayout2"
                android:orientation="vertical"
                android:layout_width="fill_parent"
                android:layout_height="100dip">
                <!--音乐名称-->
                <TextView
                    android:id="@+id/music_name"
                    android:text="无歌曲播放"
                    android:textSize="19sp"
                    android:textStyle="bold"
                    android:textColor="#ddffffff"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="11dip"
                    android:layout_marginLeft="20dip"
                    android:singleLine="true"
                    />
                <!--音乐专辑-->
                <TextView
                    android:id="@+id/music_album"
                    android:textSize="36sp"
                    android:textColor="#66ceedf9"
                    android:textStyle="bold"
                    android:layout_marginTop="28dip"
                    android:layout_marginLeft="5dip"
                    android:singleLine="true"
                    android:focusable="true"
                    android:focusableInTouchMode="true"
                    android:ellipsize="marquee"
                    android:marqueeRepeatLimit="marquee_forever"
                    android:layout_width="300dip"
                    android:layout_height="wrap_content" />
                <!--歌手名字-->
                <TextView
                    android:id="@+id/music_artist"
                    android:textSize="15sp"
                    android:textColor="#ffffff"
                    android:layout_marginTop="58dip"
                    android:layout_marginLeft="17dip"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content" />
                <!--歌曲序号-->
                <TextView
                    android:id="@+id/music_number"
                    android:text="0/0"
                    android:textSize="22sp"
                    android:textStyle="bold"
                    android:textColor="#bbf3731e"
                    android:layout_marginTop="73dip"
                    android:layout_marginRight="20dip"
                    android:gravity="center_horizontal"
                    android:layout_alignParentRight="true"
                    android:layout_width="60dip"
                    android:layout_height="wrap_content" />
            </RelativeLayout>

            <!--中间可滚动部分界面-->
            <RelativeLayout
                android:id="@+id/relativelayout1"
                android:layout_width="fill_parent"
                android:layout_height="260dip"
                android:layout_below="@+id/linearlayout2"
                >
                <com.example.liuyan.mp3play.customview.DragableLuncher
                    android:id="@+id/space"
                    liuyan:default_screen="1"
                    android:layout_width="fill_parent"
                    android:layout_height="fill_parent">
                    <!--左边显示动画界面-->
                    <include android:id="@+id/left" layout="@layout/left_mediaview"/>
                    <!--中间显示专辑界面-->
                    <include android:id="@+id/center" layout="@layout/center_special"/>
                    <!--右边显示歌词-->
                    <include android:id="@+id/right" layout="@layout/right_lrc"/>
                </com.example.liuyan.mp3play.customview.DragableLuncher>
            </RelativeLayout>

            <RelativeLayout
                android:id="@+id/relativelayout2"
                android:layout_width="fill_parent"
                android:layout_height="72dip"
                android:layout_below="@+id/relativelayout1"
                >
                <!--歌词显示进度-->
                <LinearLayout
                    android:id="@+id/linearlayout3"
                    android:gravity="center"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content">
                    <!--当前播放时间-->
                    <TextView
                        android:id="@+id/time_tv1"
                        android:text=" 00:00 "
                        android:textSize="16sp"
                        android:textStyle="bold"
                        android:textColor="#bb7af9fe"
                        android:layout_weight="1"
                        android:gravity="left"
                        android:layout_width="35dip"
                        android:layout_height="18dip" />
                    <!--显示播放进度-->
                    <SeekBar
                        android:id="@+id/player_seekbar"
                        android:progressDrawable="@drawable/seekbar_style"
                        android:background="@drawable/play_progress_background"
                        android:layout_width="220dip"
                        android:layout_height="wrap_content"
                        android:thumb="@drawable/thumb"
                        android:progress="0"
                        android:max="0"
                        />
                    <!--显示歌曲总时间-->
                    <TextView
                        android:id="@+id/time_tv2"
                        android:text=" 00:00 "
                        android:textSize="16sp"
                        android:textStyle="bold"
                        android:textColor="#bb7af9fe"
                        android:layout_weight="1"
                        android:gravity="right"
                        android:layout_width="35dip"
                        android:layout_height="18dip" />

                </LinearLayout>

                <!--控制音乐播放按钮-->
                <LinearLayout
                    android:layout_alignParentBottom="true"
                    android:layout_centerHorizontal="true"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content">
                    <!--前一首-->
                    <ImageButton
                        android:id="@+id/ib1"
                        android:src="@drawable/left_button"
                        android:background="#00000000"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content" />
                    <!--暂停播放按钮-->
                    <ImageButton
                        android:id="@+id/ib2"
                        android:background="@drawable/play_button"
                        android:layout_marginLeft="40dip"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content" />
                    <!--下一首-->
                    <ImageButton
                        android:id="@+id/ib3"
                        android:src="@drawable/right_button"
                        android:background="#00000000"
                        android:layout_marginLeft="40dip"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content" />
                </LinearLayout>

            </RelativeLayout>

        </RelativeLayout>

        <!--右边界面,显示专辑列表-->
        <include android:id="@+id/gride_special" layout="@layout/gridspecial"/>

    </com.example.liuyan.mp3play.customview.BigDragableLuncher>

    <!--底部按钮界面,用于切换歌曲列表、正在播放、专辑列表-->
    <LinearLayout
        android:orientation="horizontal"
        android:layout_alignParentBottom="true"
        android:layout_width="fill_parent"
        android:layout_height="40dip">
        <!--歌曲列表按钮-->
        <Button
            android:id="@+id/Button1"
            android:text="歌曲列表"
            android:textSize="18sp"
            android:textStyle="bold"
            android:textColor="#fafa2d"
            android:background="#00000000"
            android:layout_weight="1"
            android:layout_width="wrap_content"
            android:layout_height="match_parent" />
        <!--正在播放按钮-->
        <Button
            android:id="@+id/Button2"
            android:text="正在播放"
            android:textSize="18sp"
            android:textStyle="bold"
            android:textColor="#fafa2d"
            android:background="#00000000"
            android:layout_weight="1"
            android:layout_width="wrap_content"
            android:layout_height="match_parent" />

        <!--专辑列表按钮-->
        <Button
            android:id="@+id/Button3"
            android:text="专辑列表"
            android:textSize="18sp"
            android:textStyle="bold"
            android:textColor="#fafa2d"
            android:background="#00000000"
            android:layout_weight="1"
            android:layout_width="wrap_content"
            android:layout_height="match_parent" />
    </LinearLayout>
    <LinearLayout
        android:orientation="horizontal"
        android:layout_alignParentBottom="true"
        android:layout_width="fill_parent"
        android:layout_height="40dip">
        <ImageButton
            android:id="@+id/imageButton1"
            android:src="@drawable/big_button_style"
            android:background="#00000000"
            android:layout_weight="1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
        <ImageButton
            android:id="@+id/imageButton2"
            android:src="@drawable/big_button_style"
            android:background="#00000000"
            android:layout_weight="1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
        <ImageButton
            android:id="@+id/imageButton3"
            android:src="@drawable/big_button_style"
            android:background="#00000000"
            android:layout_weight="1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />

    </LinearLayout>


</RelativeLayout>





猜你喜欢

转载自blog.csdn.net/qq_38261174/article/details/80478066