zoukankan      html  css  js  c++  java
  • 高仿手机QQ音乐之——Android带进度条的开关

    最新版的手机QQ音乐体验确实不错,发现首页播放按钮能够显示歌曲当前进度条。认为挺有新意。效果例如以下:

    自己琢磨了下。能够用自己定义组件来实现,试着做了一下。效果例如以下:

    这里写图片描写叙述
    整理了下思路。大概设计流程是这种:
    首先,要实现音乐的关停,第一首选就是toggleButton 能够方便的控制,因此组件继承自toggleButton然后我们依据toggleButton的 isChecked的状态来 重写onDraw()方法,来绘制我们所需求的形态:
    暂停状态 播放状态
    左边的就是按钮的暂停状态,右边就是播放状态,最后在外围画一个圆标示播放的整体进度,画一段圆弧(圆弧宽度要比外围的圆宽,才干看到效果)标示当前进度,至此。构思完毕,開始写代码!
    首先,此组件应该有这样三个属性: 主体颜色。第一进度条宽度。总进度条宽度。我们先在新建一个项目 在项目res文件夹下的values文件夹中建一个 attrs.xml文件 在里面写入我们的view属性:
    attrs.xml:

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <declare-styleable name="ProgressToggleButton">
            <attr name="progress_total_width" format="dimension" />
            <attr name="progress_current_width" format="dimension" />
            <attr name="main_color" format="color" />
        </declare-styleable>
    
    </resources>

    然后我们在src文件夹下的包中建一个ProgressToggleButton 继承自ToggleButton 并在构造方法中获得我们自己定义的属性:

    public class ProgressToggleButton extends ToggleButton {
        public ProgressToggleButton(Context context) {
            super(context);
        }
        private int mainColor;//主体颜色
    
        private int circleTotalWidth, circleCurrentWidth;//总进度和当前进度的宽度
    
        public ProgressToggleButton(Context context, AttributeSet attrs) {
            super(context, attrs);
            TypedArray arry = context.obtainStyledAttributes(attrs,
                    R.styleable.ProgressToggleButton);
            mainColor = arry.getColor(R.styleable.ProgressToggleButton_main_color,
                    getResources().getColor(R.color.main_color));//前面值就是xml文件指定的值,后者就是前者未指定的默认值
            circleTotalWidth = (int) arry.getDimension(
                    R.styleable.ProgressToggleButton_progress_total_width,
                    getResources().getDimension(R.dimen.progress_total_width));
            circleCurrentWidth = (int) arry.getDimension(
                    R.styleable.ProgressToggleButton_progress_current_width,
                    getResources().getDimension(R.dimen.progress_current_width));
            arry.recycle();//一定要让recycle 否则会出问题
        }
    }

    然后再重写onDraw()方法绘出自己要的属性:

    @Override
        protected void onDraw(Canvas canvas) {
            mPaint.setColor(mainColor);
            mPaint.setAntiAlias(true);
            mPaint.setStrokeWidth(0);
            int center = getWidth() / 2;// 三角形,圆圈中央
            int sideLength = center / 5 * 4; // 三角形边长
            if (this.isChecked()) {
                drawPlay(canvas, center, sideLength);//绘制两条竖线
            } else {
                drawStop(canvas, center, sideLength);//绘制正三角形
            }
            // 最外围的总进度
            mPaint.setStrokeWidth(circleTotalWidth);
            mPaint.setStyle(Paint.Style.STROKE);
            int radius = center - circleTotalWidth;
            canvas.drawCircle(center, center, radius, mPaint);
            // 最当前进度
            RectF oval = new RectF(center - radius + circleTotalWidth, center
                    - radius + circleTotalWidth,
                    center + radius - circleTotalWidth, center + radius
                            - circleTotalWidth);
            mPaint.setStrokeWidth(circleCurrentWidth);
            canvas.drawArc(oval, -90, mProgress, false, mPaint);//mProgress指圆弧的度数,这里就代表了我们的进度
        }

    这里的drawPlay方法没有给出。等等在具体代码中展示。以下是监听,
    为了方便我们使用,我们又一次写一个接口,在我们使用的时候回调:

    public interface onCheckChangesListener {
    
            void onchechkchanges(boolean isChecked);
        }
    
        private onCheckChangesListener listener;
    
        /**
         * 监听
         * 
         * @param l
         */
        public void setOnCheckChangesListener(onCheckChangesListener l) {
            this.listener = l;
        }

    在构造方法中调用自带的OnCheckChangeLister:

    this.setOnCheckedChangeListener(new OnCheckedChangeListener() {
    
                @Override
                public void onCheckedChanged(CompoundButton buttonView,
                        boolean isChecked) {
                    if (listener != null) {
                        listener.onchechkchanges(isChecked);//调接口中的方法
                    }
                    postInvalidate();//刷新界面
                }
            });

    好了。来看看先阶段的效果:
    主布局文件:

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:progres="http://schemas.android.com/apk/res/com.example.progrestogglebutton"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
    
        <com.example.progrestogglebutton.weight.ProgressToggleButton
            android:id="@+id/user_div"
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:layout_alignParentRight="true"
            android:layout_margin="10dp"
            android:background="@color/transparent_color"
            progres:main_color="#FAA532"
            progres:progress_current_width="5dp"
            progres:progress_total_width="2dp"
            tools:ignore="RtlHardcoded" />
    
        <com.example.progrestogglebutton.weight.ProgressToggleButton
            android:id="@+id/defult"
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:layout_margin="10dp"
            android:background="@color/transparent_color"
            tools:ignore="RtlHardcoded" />
    
        <com.example.progrestogglebutton.weight.ProgressToggleButton
            android:id="@+id/play"
            android:layout_width="52dp"
            android:layout_height="52dp"
            android:layout_alignParentBottom="true"
            android:layout_centerHorizontal="true"
            android:layout_margin="10dp"
            android:background="@color/transparent_color"
            tools:ignore="RtlHardcoded" />
    
        <SeekBar
            android:id="@+id/seekbar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_above="@+id/play"
            android:max="100"
            android:progress="0" />
    
    </RelativeLayout>

    主Activity:

    package com.example.progrestogglebutton;
    
    import com.example.progrestogglebutton.weight.ProgressToggleButton;
    import com.example.progrestogglebutton.weight.ProgressToggleButton.onCheckChangesListener;
    
    import android.app.Activity;
    import android.media.MediaPlayer;
    import android.os.AsyncTask;
    import android.os.Bundle;
    import android.util.Log;
    import android.widget.SeekBar;
    import android.widget.SeekBar.OnSeekBarChangeListener;
    import android.widget.Toast;
    
    public class MainActivity extends Activity {
        private ProgressToggleButton tDefult, tDiv, tplayer;
    
        private MediaPlayer mMediaPlayer;
    
        private SeekBar mSeekBar;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            init();
        }
    
        private void init() {
            tDefult = (ProgressToggleButton) findViewById(R.id.defult);
            tDiv = (ProgressToggleButton) findViewById(R.id.user_div);
            tplayer = (ProgressToggleButton) findViewById(R.id.play);
            mSeekBar = (SeekBar) findViewById(R.id.seekbar);
            mMediaPlayer = MediaPlayer.create(MainActivity.this, R.raw.penta_kill);
            tDefult.setOnCheckChangesListener(new onCheckChangesListener() {
    
                @Override
                public void onchechkchanges(boolean isChecked) {
                    Toast.makeText(MainActivity.this, "默认样式被点击,状态为" + isChecked,
                            Toast.LENGTH_SHORT).show();
                }
            });
            tDiv.setOnCheckChangesListener(new onCheckChangesListener() {
    
                @Override
                public void onchechkchanges(boolean isChecked) {
                    Toast.makeText(MainActivity.this, "用户指定样式被点击,状态为" + isChecked,
                            Toast.LENGTH_SHORT).show();
                }
            });
            tplayer.setOnCheckChangesListener(new onCheckChangesListener() {
    
                @Override
                public void onchechkchanges(boolean isChecked) {
                    if (musicAsyTask != null) {
                        musicAsyTask.cancel(true);
                    }
                    musicAsyTask = (MusicAsyTask) new MusicAsyTask().execute();
                    if (isChecked) {
                        mMediaPlayer.start();
                    } else {
                        mMediaPlayer.pause();
                    }
                }
            });
            mSeekBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
    
                @Override
                public void onStopTrackingTouch(SeekBar seekBar) {
                    musicAsyTask = (MusicAsyTask) new MusicAsyTask().execute();
                    Log.d("LOG", "finish");
    
                }
    
                @Override
                public void onStartTrackingTouch(SeekBar seekBar) {
                    if (musicAsyTask != null) {
                        musicAsyTask.cancel(true);
                        musicAsyTask = null;
                        Log.d("LOG", "starttoch");
                    }
                }
    
                @Override
                public void onProgressChanged(SeekBar seekBar, int progress,
                        boolean fromUser) {
                    if (fromUser) {
                        Log.d("LOG", progress + "");
                        mMediaPlayer.seekTo((progress * mMediaPlayer.getDuration()) / 100);
                        tplayer.setProgress(progress);
                    }
    
                }
            });
        }
    
        private MusicAsyTask musicAsyTask;
    
        public class MusicAsyTask extends AsyncTask<Void, Integer, Void> {
    
            @Override
            protected Void doInBackground(Void... params) {
                publishProgress(getPercent(mMediaPlayer.getCurrentPosition(),
                        mMediaPlayer.getDuration()));
                try {
                    Thread.sleep(1000);
                    this.doInBackground();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return null;
            }
    
            @Override
            protected void onProgressUpdate(Integer... values) {
                int progress = values[0];
                tplayer.setProgress(progress);
                mSeekBar.setProgress(progress);
                super.onProgressUpdate(values);
            }
    
        }
    
        /**
         * 计算百分比
         * 
         * @param progress
         *            当前进度
         * @param total
         *            总进度
         * @return
         */
        public int getPercent(int progress, int total) {
            double baiy = progress * 1.0;
            double baiz = total * 1.0;
            double fen = baiy / baiz;
            return (int) (fen * 100);
        }
    
    }
    

    来看看效果:
    这里写图片描写叙述
    细节阐述:
    1.在xml文件里,每一个ProgressToggleButton,我都将背景指定为全然透明的 颜色,android:background=”@color/transparent_color”,以覆盖系统自带的样式。

    2.左边的为默认样式,右边的在布局文件里指定了自己的属性,这里要注意的时。假设你要引入自己定义属性,就一定要在xml中引入xmlns:progres=”http://schemas.android.com/apk/res/com.example.progrestogglebutton” 这是调用了此布局文件的 activity所在的包路径

    3.至于设置进度,实现方案就是 在当中加入setProgress()方法,依据设置的值,来确定第二进度的值,在activity中借助 handler+thread 或者 asytask 动态获取 播放媒体时间的进度 动态设置给progressToggleButton就可以!

    完整组件代码:

    
    import android.annotation.SuppressLint;
    import android.content.Context;
    import android.content.res.TypedArray;
    import android.graphics.Canvas;
    import android.graphics.Paint;
    import android.graphics.Path;
    import android.graphics.RectF;
    import android.util.AttributeSet;
    import android.widget.CompoundButton;
    import android.widget.ToggleButton;
    
    import com.example.progrestogglebutton.R;
    
    @SuppressLint("DrawAllocation")
    public class ProgressToggleButton extends ToggleButton {
        public interface onCheckChangesListener {
    
            void onchechkchanges(boolean isChecked);
        }
    
        private onCheckChangesListener listener;
    
        /**
         * 监听
         * 
         * @param l
         */
        public void setOnCheckChangesListener(onCheckChangesListener l) {
            this.listener = l;
        }
    
        private Paint mPaint;
    
        private int mainColor;
    
        private int circleTotalWidth, circleCurrentWidth;
    
        private int mProgress = 1;// 当前进度
    
        public ProgressToggleButton(Context context) {
            super(context);
        }
    
        public ProgressToggleButton(Context context, AttributeSet attrs) {
            super(context, attrs);
            mPaint = new Paint();
            TypedArray arry = context.obtainStyledAttributes(attrs,
                    R.styleable.ProgressToggleButton);
            mainColor = arry.getColor(R.styleable.ProgressToggleButton_main_color,
                    getResources().getColor(R.color.main_color));
            circleTotalWidth = (int) arry.getDimension(
                    R.styleable.ProgressToggleButton_progress_total_width,
                    getResources().getDimension(R.dimen.progress_total_width));
            circleCurrentWidth = (int) arry.getDimension(
                    R.styleable.ProgressToggleButton_progress_current_width,
                    getResources().getDimension(R.dimen.progress_current_width));
            arry.recycle();
            this.setOnCheckedChangeListener(new OnCheckedChangeListener() {
    
                @Override
                public void onCheckedChanged(CompoundButton buttonView,
                        boolean isChecked) {
                    if (listener != null) {
                        listener.onchechkchanges(isChecked);
                    }
                    postInvalidate();
                }
            });
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            mPaint.setColor(mainColor);
            mPaint.setAntiAlias(true);
            mPaint.setStrokeWidth(0);
            int center = getWidth() / 2;// 三角形,圆圈中央
            int sideLength = center / 5 * 4; // 三角形边长
            if (this.isChecked()) {
                drawPlay(canvas, center, sideLength);
            } else {
                drawStop(canvas, center, sideLength);
            }
            // 最外围的总进度
            mPaint.setStrokeWidth(circleTotalWidth);
            mPaint.setStyle(Paint.Style.STROKE);
            int radius = center - circleTotalWidth;
            canvas.drawCircle(center, center, radius, mPaint);
            // 最当前进度
            RectF oval = new RectF(center - radius + circleTotalWidth, center
                    - radius + circleTotalWidth,
                    center + radius - circleTotalWidth, center + radius
                            - circleTotalWidth);
            mPaint.setStrokeWidth(circleCurrentWidth);
            canvas.drawArc(oval, -90, mProgress, false, mPaint);
        }
    
        /**
         * 画暂停状态
         * 
         * @param canvas
         * @param center
         *            三角形中心横纵坐标
         * @param sideLength
         *            三角形边长
         */
        private void drawStop(Canvas canvas, int center, int sideLength) {
            mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
            float genSan = (float) Math.sqrt(3);
            Path path2 = new Path();
            path2.moveTo((center - sideLength / (2 * genSan)), center - sideLength
                    / 2);
            path2.lineTo((center + 2 * sideLength / (2 * genSan)), center);
            path2.lineTo((center - sideLength / (2 * genSan)), center + sideLength
                    / 2);
            path2.close();
            canvas.drawPath(path2, mPaint);
        }
    
        /**
         * 画播放状态
         * 
         * @param canvas
         * @param center
         *            两条线的对称轴中心横纵坐标
         * @param sideLength
         *            线的长度
         */
    
        private void drawPlay(Canvas canvas, int center, int sideLength) {
            float genSan = (float) Math.sqrt(3);
            float linesWidth = sideLength / 5;
            mPaint.setStrokeWidth(linesWidth);
            canvas.drawLine((center - sideLength / (2 * genSan)) + linesWidth / 2,
                    center - sideLength / 2, (center - sideLength / (2 * genSan))
                            + linesWidth / 2, center + sideLength / 2, mPaint);
            canvas.drawLine((center + sideLength / (2 * genSan)) - linesWidth / 2,
                    center - sideLength / 2, (center + sideLength / (2 * genSan))
                            - linesWidth / 2, center + sideLength / 2, mPaint);
        }
    
        /**
         * 设置进度
         * 
         * @param progress
         */
    
        public void setProgress(int progress) {
            if (progress >= 100) {
                mProgress = 360 ;
            } else {
                mProgress = (int) ((progress + 1) * 3.6);
            }
            postInvalidate();
        }
    
        public int getProgress() {
            return (int) (mProgress / 3.6);
        }
    
        // 设置为wrap_content 时的控件高宽
        private int defultWidth = (int) getResources().getDimension(
                R.dimen.weidght_size);
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            int widthMode = MeasureSpec.getMode(widthMeasureSpec);
            int widthSize = MeasureSpec.getSize(widthMeasureSpec);
            int heightMode = MeasureSpec.getMode(heightMeasureSpec);
            int heightSize = MeasureSpec.getSize(heightMeasureSpec);
            int finalWidth = 0;
            int finaLHeight = 0;
            if (widthMode == MeasureSpec.EXACTLY) {
                finalWidth = widthSize;
            } else {
                finalWidth = (int) (getPaddingLeft() + defultWidth + getPaddingRight());
            }
            if (heightMode == MeasureSpec.EXACTLY) {
                finaLHeight = heightSize;
            } else {
                finaLHeight = (int) (getPaddingTop() + defultWidth + getPaddingBottom());
            }
            setMeasuredDimension(finalWidth, finaLHeight);
        }
    }

    总结:
    1.本人技术有限,又第一次写博客。肯定有非常多纰漏之处,忘广大网友批评斧正,共同进步。
    2.本组件事实上挺简单,核心就是自己定义组件,可是在研究过程中,那个画三角形确实花了点时间,要用三角函数来确定坐标,终于还是弄出来了,所以在这里分享出来,分享才是一种快乐。

    点击源代码下载

  • 相关阅读:
    SD卡测试
    测试人员可能会遇到的问题
    HDU 1024 Max Sum Plus Plus
    HDU 1176 免费馅饼
    HDU 1257 最少拦截系统
    HDU 1087 Super Jumping! Jumping! Jumping!
    poj 1328 Radar Installation
    poj 1753 Flip Game
    HDU 1003 Max Sum
    HDU 5592 ZYB's Premutation(BestCoder Round #65 C)
  • 原文地址:https://www.cnblogs.com/cxchanpin/p/7261781.html
Copyright © 2011-2022 走看看