• 高仿手机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.本组件事实上挺简单,核心就是自己定义组件,可是在研究过程中,那个画三角形确实花了点时间,要用三角函数来确定坐标,终于还是弄出来了,所以在这里分享出来,分享才是一种快乐。

    点击源代码下载

  • 相关阅读:
    Hive metastore三种配置方式
    hive学习(一)hive架构及hive3.1.1三种方式部署安装
    hiveserver2的配置和启动
    spark安装配置
    Sqoop-1.4.6安装部署及详细使用介绍
    搭建本地yum源服务器
    Centos7.X安装impala(RPM方式)
    Hive安装与配置详解
    【图文详解】Hadoop集群搭建(CentOs6.3)
    Linux下实现免密码登录(超详细)
  • 原文地址:https://www.cnblogs.com/cxchanpin/p/7261781.html
Copyright © 2020-2023  润新知