最新版的手机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()方法绘出自己要的属性:

@Overrideprotected 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() {@Overridepublic 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.ProgressToggleButtonandroid: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.ProgressToggleButtonandroid: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.ProgressToggleButtonandroid: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" /><SeekBarandroid: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;@Overrideprotected 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() {@Overridepublic void onchechkchanges(boolean isChecked) {Toast.makeText(MainActivity.this, "默认样式被点击,状态为" + isChecked,Toast.LENGTH_SHORT).show();}});tDiv.setOnCheckChangesListener(new onCheckChangesListener() {@Overridepublic void onchechkchanges(boolean isChecked) {Toast.makeText(MainActivity.this, "用户指定样式被点击,状态为" + isChecked,Toast.LENGTH_SHORT).show();}});tplayer.setOnCheckChangesListener(new onCheckChangesListener() {@Overridepublic 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() {@Overridepublic void onStopTrackingTouch(SeekBar seekBar) {musicAsyTask = (MusicAsyTask) new MusicAsyTask().execute();Log.d("LOG", "finish");}@Overridepublic void onStartTrackingTouch(SeekBar seekBar) {if (musicAsyTask != null) {musicAsyTask.cancel(true);musicAsyTask = null;Log.d("LOG", "starttoch");}}@Overridepublic 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> {@Overrideprotected Void doInBackground(Void... params) {publishProgress(getPercent(mMediaPlayer.getCurrentPosition(),mMediaPlayer.getDuration()));try {Thread.sleep(1000);this.doInBackground();} catch (InterruptedException e) {e.printStackTrace();}return null;}@Overrideprotected 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() {@Overridepublic void onCheckedChanged(CompoundButton buttonView,boolean isChecked) {if (listener != null) {listener.onchechkchanges(isChecked);}postInvalidate();}});}@Overrideprotected 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);@Overrideprotected 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带进度条的开关相关推荐

  1. android 上传图片进度条,Android带进度条的文件上传示例(使用AsyncTask异步任务)...

    最近项目中要做一个带进度条的上传文件的功能,学习了AsyncTask,使用起来比较方便,将几个方法实现就行,另外做了一个很简单的demo,希望能对大家有帮助,在程序中设好文件路径和服务器IP即可. d ...

  2. zepto+less写QQ音乐播放界面,进度条同步,歌词同步高亮等等(带注释,可参考可直接使用)

    这是效果图,小编截下来的是静态图片,真是都是可以动的 不多说,直接上代码,注释都写在代码里清清楚楚 less @sizeMan:100%; //关键帧动画函数 .keyframes(@name:mov ...

  3. C#/音乐播放器/带进度条/歌词滚动、颜色变化/桌面应用程序设计

    用基本C#知识实现制作一个音乐播放器 前言 写这个博客并不是说我的作品多么高级或完美,只是希望能在一些功能方面给你们一些启发,能帮助到你们做出真正好的程序,这就足够了 话不多说,让我们开始吧~~~ 截 ...

  4. 微信小程序:音乐播放器带进度条

    代码 JS // pages/meddledetails/meddledetails.js var config = require('../../config.js') var util = req ...

  5. Android之高仿手机QQ聊天

    源代码下载 转载请注明出处,谢谢! 最终版已上传.优化下拉刷新.增加来消息声音提示.主界面改成ViewPager,实现左右滑动.新增群组.最近会话显示条数,开始上班了,不再修改了.谢谢! 国庆这几天, ...

  6. Android之高仿手机QQ图案解锁

    本文源码(utf-8编码):http://download.csdn.net/detail/weidi1989/6628211 ps:请不要再问我,为什么导入之后会乱码了. 其实,代码基本上都是从原生 ...

  7. 高仿网易云音乐界面 android特效

    仿照网易云音乐界面 ,页面UI实现的听不错的,学习ui的朋友可以下载下来研究研究, 项目大体框架,由ViewPager和TabContentPagerAdapter实现顶部的左右滑动切换view. 右 ...

  8. android高仿酷狗音乐播放器源码下载

    这是一款简单的读取SD卡音乐文件进行播放.暂停.删除.切歌等功能的高仿酷狗音乐播放器. 主要功能: 模块 简要说明 扫描SD卡音乐 扫描SD卡,并显示出本地音乐列表 提供歌词跟随音乐滚动更能   采用 ...

  9. android 仿网易标签切换,高仿网易云音乐客户端的Home页面切换Tabhost-IT蓝豹

    1.高仿网易云音乐客户端的Home页面切换Tabhost 高仿网易云音乐客户端的Home页面切换Tabhost,并且三角形是透明的, 实现方式,自定义AnimTabsView继承 RelativeLa ...

最新文章

  1. 基于深度学习的特征提取和匹配方法介绍
  2. HDU 4405 概率期望DP
  3. 首批互联网地图服务牌照发放 图吧地图获得甲级服务资质
  4. Design Patterns之Adapter Pattern总结
  5. 机器人出魔切还是三相_英雄联盟:辅助也要去上单,机器人布里茨玩法介绍
  6. 通过js适配不同的屏幕大小
  7. ②⓪②⓪ → ②⓪②①
  8. oracle中的存储过程和存储函数的区别
  9. 技术分享 | 如何在无人机设计中运用拓补优化技术?
  10. 你需要来自TrustedInstaller的权限才能对此文件夹进行更改(已解决)
  11. 计算机流程图设计教程,流程图制作工具分享,在电脑上就可以画图
  12. html网页背景图片 菜鸟教程,CSS3 背景
  13. 应对安全漏洞:如何将LFI变为RFI
  14. R.layout是什么?
  15. jsp的taglib指令用法
  16. Hi3518EV200的开发环境搭建,Hi3518E_V200R001C01SPC030
  17. ApproximateVoxelGrid和VoxelGrid详解
  18. FULENT软件测试工程师头像,关于ANSYS总部测试案例3之FLUENT软件中湍流管道压降的测试报告20141230.pdf...
  19. 量化考核指标的TQC法
  20. 微信小程序 连接云数据库(不使用云函数)进行 登录、注册、查询(包括模糊查询)快速实现 亲测可用

热门文章

  1. 【2021秋招】Java 面试知识点【精华背诵版】
  2. STM32F103驱动GY-30(BH1750)光照强度传感器程序讲解(附加程序下载)
  3. 百度翻译日活大涨40%:技术十年沉淀,爆发恰逢其时
  4. 什么是负载均衡(SLB)
  5. 微信小程序用python语言开发_微信小程序语音识别开发过程笔记
  6. Mac安装使用Virtualenv
  7. VMware开启虚拟机提示“内部错误”
  8. [blender]UV网格颜色图片上色以及多材质上色
  9. Vue项目配置git忽略文件
  10. android shell卸载应用程序,Android adb 查看已经安装的应用、安装应用、卸载应用...