高仿手机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.本组件事实上挺简单,核心就是自己定义组件,可是在研究过程中,那个画三角形确实花了点时间,要用三角函数来确定坐标,终于还是弄出来了,所以在这里分享出来,分享才是一种快乐。
高仿手机QQ音乐之——Android带进度条的开关的更多相关文章
- 025 Android 带进度条的对话框(ProgressDialog)
		1.ProgressDialog介绍 ProgressDialog可以在当前界面弹出一个置顶于所有界面元素的对话框,同样具有屏蔽其他控件的交互能力,用于提示用户当前操作正在运行,让用户等待: 2.应用 ... 
- Android带进度条的文件上传,使用AsyncTask异步任务
		最近项目中要做一个带进度条的上传文件的功能,学习了AsyncTask,使用起来比较方便,将几个方法实现就行,另外做了一个很简单的demo,希望能对大家有帮助,在程序中设好文件路径和服务器IP即可. A ... 
- Android之高仿手机QQ聊天
		源代码下载 转载请注明出处,谢谢! 最终版已上传.优化下拉刷新.增加来消息声音提示.主界面改成ViewPager,实现左右滑动.新增群组.最近会话显示条数,开始上班了,不再修改了.谢谢! 国庆这几天, ... 
- Android高级控件(六)——自定义ListView高仿一个QQ可拖拽列表的实现
		Android高级控件(六)--自定义ListView高仿一个QQ可拖拽列表的实现 我们做一些好友列表或者商品列表的时候,居多的需求可能就是需要列表拖拽了,而我们选择了ListView,也是因为使用L ... 
- 新鲜出炉高仿网易云音乐 APP
		我的引语 晚上好,我是吴小龙同学,我的公众号「一分钟GitHub」会推荐 GitHub 上好玩的项目,一分钟 get 一个优秀的开源项目,挖掘开源的价值,欢迎关注我. 项目中成长是最快的,如何成长,就 ... 
- vue-miniQQ——基于Vue2实现的仿手机QQ单页面应用(接入了聊天机器人,能够进行正常对话)
		使用Vue2进行的仿手机QQ的webapp的制作,作品由个人独立开发,源码中进行了详细的注释. 由于自己也是初学Vue2,所以注释写的不够精简,请见谅. 项目地址 https://github.com ... 
- Android -- 自定义带进度条的按钮
		1. 实现了一个带进度条的按钮,完成后显示提示信息,并设置按钮为不可再次被点击 
- Android更新带进度条的通知栏
		在网上查询了下.Android版本号更新通知栏带进度条,醉了,基本都是复制过来.有的代码不全,连源代码下载都没有.有下载也须要积分,还不能用,真黑心啊!!之前自己也写过自己定义通知栏Notificat ... 
- 自定义带进度条的WebView , 增加获取web标题和url 回掉
		1.自定义ProgressWebView package com.app.android05; import android.content.Context; import android.graph ... 
随机推荐
- 【 Linux 】I/O工作模型及Web服务器原理
			一.进程.线程 进程是具有一定独立功能的,在计算机中已经运行的程序的实体.在早期系统中(如linux 2.4以前),进程是基本运作单位,在支持线程的系统中(如windows,linux2.6) ... 
- fullpage.js jq全屏滚动插件
			fullPage.js和fullPage都能实现全屏滚动,二者区别是:fullPage.js需依赖于JQuery库,而fullPage不需要依赖任何一个js库,可以单独使用. (代码演示效果并且可以下 ... 
- 在另一个文本框显示input file选择的文件名字
			javascript 获取文件域 (type=file) 的完整路径一直是很麻烦的问题,问题主要出在一些浏览器基于安全性考虑而不能正常获取到文件域中选中图片的决对路径,尤其一些基于webkit的浏览器 ... 
- 前端基础BOM和DOM
			前言 到目前为止,我们已经学过了JavaScript的一些简单的语法.但是这些简单的语法,并没有和浏览器有任何交互. 也就是我们还不能制作一些我们经常看到的网页的一些交互,我们需要继续学习BOM和DO ... 
- [DB2]Linux下安装db2 v9.7
			https://www.cnblogs.com/cancer-sun/p/5168728.html 
- ionic3 打包发布,以安卓说明
			1 添加图标 ionic cordova resources [<platform>] 官方https://ionicframework.com/docs/cli/cordova/reso ... 
- Codeforces 731 C.Socks-并查集+STL(vector+map)
			C. Socks time limit per test 2 seconds memory limit per test 256 megabytes input standard input ... 
- (7)python 函数和lambda表达式
			一.函数定义和调用 1.定义函数用def 2.函数注释 在函数体里的开头写上字符串,可以起到说明的作用 可以用函数名.__doc__的方式读取在函数开头加的字符串(双下划线) 内建的help()函数也 ... 
- floyed算法的一些感想
			for(int k=1;k<=n;k++) for(int i=1;i<=n;i++) for(int j=1;j<=k;j++) if(f[i][k]+f[k][j]<f[i ... 
- Codeforces Round #394 (Div. 2) C. Dasha and Password(简单DP)
			C. Dasha and Password time limit per test 2 seconds memory limit per test 256 megabytes input standa ... 
