最新版的手机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带进度条的开关的更多相关文章

  1. 025 Android 带进度条的对话框(ProgressDialog)

    1.ProgressDialog介绍 ProgressDialog可以在当前界面弹出一个置顶于所有界面元素的对话框,同样具有屏蔽其他控件的交互能力,用于提示用户当前操作正在运行,让用户等待: 2.应用 ...

  2. Android带进度条的文件上传,使用AsyncTask异步任务

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

  3. Android之高仿手机QQ聊天

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

  4. Android高级控件(六)——自定义ListView高仿一个QQ可拖拽列表的实现

    Android高级控件(六)--自定义ListView高仿一个QQ可拖拽列表的实现 我们做一些好友列表或者商品列表的时候,居多的需求可能就是需要列表拖拽了,而我们选择了ListView,也是因为使用L ...

  5. 新鲜出炉高仿网易云音乐 APP

    我的引语 晚上好,我是吴小龙同学,我的公众号「一分钟GitHub」会推荐 GitHub 上好玩的项目,一分钟 get 一个优秀的开源项目,挖掘开源的价值,欢迎关注我. 项目中成长是最快的,如何成长,就 ...

  6. vue-miniQQ——基于Vue2实现的仿手机QQ单页面应用(接入了聊天机器人,能够进行正常对话)

    使用Vue2进行的仿手机QQ的webapp的制作,作品由个人独立开发,源码中进行了详细的注释. 由于自己也是初学Vue2,所以注释写的不够精简,请见谅. 项目地址 https://github.com ...

  7. Android -- 自定义带进度条的按钮

    1. 实现了一个带进度条的按钮,完成后显示提示信息,并设置按钮为不可再次被点击

  8. Android更新带进度条的通知栏

    在网上查询了下.Android版本号更新通知栏带进度条,醉了,基本都是复制过来.有的代码不全,连源代码下载都没有.有下载也须要积分,还不能用,真黑心啊!!之前自己也写过自己定义通知栏Notificat ...

  9. 自定义带进度条的WebView , 增加获取web标题和url 回掉

    1.自定义ProgressWebView package com.app.android05; import android.content.Context; import android.graph ...

随机推荐

  1. WCF发布方式介绍

    转载出处:http://blog.csdn.net/fangxing80/article/details/6101790 从VS2005推出WCF以来,WCF逐步取代了Remoting, WebSer ...

  2. 关于ros stage与navigation仿真总结5月16号

    主要总结内容 在costmap里是怎么判断机器人和障碍物碰撞了 stage_ros包输入输出,stage是怎么回事 rviz 中footprint和stage中position怎么联系到一起 voxe ...

  3. docker1.13新功能上要关注的点

    如果要作单点端口映射,则需要结合constraint和label来定位具体的proxy机器吧. 如果不用这种模式,,ingress确实又太浪费集群端口了.. 纠结,,看看如何和compose v3作很 ...

  4. [BZOJ3698] XWW的难题 网络流

    3698: XWW的难题 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 533  Solved: 275[Submit][Status][Discus ...

  5. mysql 文本搜索

    全文本搜索 MySQL支持几种基本的数据库引擎,但并非所有的引擎都支持全文本搜索.两个最常使用的引擎为MyISAM和InnoDB,前者支持全文本搜索,后者就不支持. 理解全文本搜索 在前面的学习中,我 ...

  6. sed 概述

    sed 是一种在线编辑器,它一次处理一行内容.处理时,把当前处理的行存储在临时缓冲区中,称为“模式空间”(pattern space),接着用sed命令处理缓冲区中的内容,处理完成后,把缓冲区的内容送 ...

  7. [BZOJ 3157] 国王奇遇记

    Link: BZOJ 3157 传送门 Solution: 题意:求解$\sum_{i=1}^n m^i \cdot {i^m}$ $O(m^2)$做法: 定义一个函数$f[i]$,$f[i]=\su ...

  8. POJ 2763 Housewife Wind(树链剖分+树状数组)

    [题目链接] http://poj.org/problem?id=2763 [题目大意] 在一棵树上,给出一些边的边长,有修改边的边长的操作, 询问每次从当前点到目标点的最短距离 [题解] 树链剖分之 ...

  9. 【树状数组】bzoj2789 [Poi2012]Letters

    处理数组A,A[i]表示字符串a的第i个字符排序后应去的位置,队列可. 对于多次出现的字符,其在a中的顺序和在b中的顺序应该是一一对应的. #include<cstdio> #includ ...

  10. Android之Activity 生命周期

    作用:用户界面的组件,主要用于和用户进行交互.可以理解为手机屏幕的一屏. 生命周期: Resume:“继续”的意思. 由此可见, Activity有四种基本状态: 1) Running:位于屏幕最前端 ...