• Android 自定义View,仿微信视频播放按钮


    闲着,尝试实现了新版微信视频播放按钮,使用的是自定义View,先来个简单的效果图。。。真的很简单哈。

    由于暂时用不到,加上时间原因,加上实在是没意思,加上……,本控件就没有实现自定义属性,有兴趣的朋友可以自己去添加一下,方法都给你们准备好了。- =

    其实这个控件主要步骤

    1、画外环的圆

    2、画进度的圆或者画三角形播放按钮

    其余剩下的都是围绕以上两步准备或者收尾的。

    接下来贴主要我们的自定义控件代码,注释很全,我就不过多解释了,请各位看官自己分析,有疑问可以在评论区一起讨论。

    package com.lwd.playbutton;
    
    import android.content.Context;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.Paint;
    import android.graphics.Path;
    import android.graphics.RectF;
    import android.util.AttributeSet;
    import android.util.TypedValue;
    import android.view.View;
    import android.view.View.OnClickListener;
    /**
     * 仿微信视频播放按钮
     * @author Vitor Lee
     */
    public class PlayButton extends View implements OnClickListener {
        /**默认最大角度*/
        private static final int DEFAULT_MAX_ANGLE = 360;
        /**默认最大的进度*/
        private static final int DEFAULT_MAX_PROGRESS=100;
        /**描边宽度*/
        private int mStrokeWidth;
        /**外圆环半径*/
        private int mOutRadius;
        /**内圆半径*/
        private int mInnerRiadius;
        /**控件的宽度*/
        private int mWidth;
        /**控件的高度*/
        private int mHeight;
        /**描边的画笔*/
        private Paint mStrokePaint;
        /**实心画笔*/
        private Paint mFillPaint;
        /**进度圆的*/
        private RectF mProgressOval;
        /**最大进度*/
        private int mMax=DEFAULT_MAX_PROGRESS;
        /**当前进度*/
        private int mProgress;
        /**三角形的路径*/
        private Path mTriangle;
        private ProgressState mCurrentState=ProgressState.PRE_START;
        
        public PlayButton(Context context) {
            this(context,null);
        }
        
        public PlayButton(Context context, AttributeSet attrs) {
            this(context, attrs,0);
        }
        
        public PlayButton(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
            initParams();
            initAttribute(context, attrs, defStyle);
        }
    
        private void initParams() {
            mStrokeWidth = (int) TypedValue.applyDimension(
                    TypedValue.COMPLEX_UNIT_SP, 1, getResources()
                            .getDisplayMetrics());
            
            //初始化描边的笔
            mStrokePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
            mStrokePaint.setColor(Color.WHITE);
            mStrokePaint.setStyle(Paint.Style.STROKE);
            mStrokePaint.setStrokeWidth(mStrokeWidth);
            
            //初始化画实心的笔
            mFillPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
            mFillPaint.setColor(Color.WHITE);
            mFillPaint.setStyle(Paint.Style.FILL);
            
            setOnClickListener(this);
        }
    
        private void initAttribute(Context context, AttributeSet attrs, int defStyle) {
            //TODO 增加自定义属性,解析应用自定义属性
        }
        
        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);
            mWidth = w;
            mHeight = h;
            
            //计算外环的半径 得到控件宽高的最小值作为圆的半径,还要减去掉描边的宽度
            mOutRadius = (Math.min(w, h))/2-mStrokeWidth;
            //计算进度圆的半径,减去两倍描边宽度,作为进度圆和外圆环之间的间隙
            mInnerRiadius =mOutRadius-2*mStrokeWidth;
            //确定进度圆的范围
            mProgressOval = new RectF(mWidth / 2 - mInnerRiadius, mHeight / 2
                    - mInnerRiadius, mWidth / 2 + mInnerRiadius, mHeight
                    / 2 + mInnerRiadius);
            
            int triangleHeight = mOutRadius/3;
            //用三个点来确定三角形的位置,这里以外圆环直径的1/3作为三角形的水平方向的高度,
            //水平方向向右做了 1/2高度的偏移,让三角形中心与圆的中心重叠(从视觉上来说是中心了,从科学的角度来讲这里应该不是中心,博主数学基础不扎实。。)
            mTriangle = new Path();
            mTriangle.moveTo(w/2-triangleHeight/2,w/2-triangleHeight);
            mTriangle.lineTo(w/2+triangleHeight+triangleHeight/2,h/2);
            mTriangle.lineTo(w/2-triangleHeight/2,w/2+triangleHeight);
            mTriangle.close();
            //等边三角形
    //        mRantange = new Path();
    //        float halfOfRantangeHeight = (float) (Math.sqrt(1f/27*Math.pow(mOutRadius*2,2)));
    //        Log.e("xxx","mOutRadius/3="+mOutRadius/3+" ,halfOfRantangeHeight="+halfOfRantangeHeight);
    //        mRantange.moveTo(w/2-mOutRadius/6,h/2-halfOfRantangeHeight);
    //        mRantange.lineTo(w/2+mOutRadius/3+mOutRadius/6,h/2);
    //        mRantange.lineTo(w/2-mOutRadius/6,h/2+halfOfRantangeHeight);
    //        mRantange.close();
            
        }
        
        @Override
        protected void onDraw(Canvas canvas) {
            //绘制外圆环
            canvas.drawCircle(mWidth/2,mHeight/2,mOutRadius,mStrokePaint);
            if (mCurrentState==ProgressState.RUNNING) {//运行状态,绘制进度圆
                canvas.drawArc(mProgressOval,-90,(mProgress*1f/mMax*DEFAULT_MAX_ANGLE),true,mFillPaint);
            }else{//非运行状态画三角形
                canvas.drawPath(mTriangle,mStrokePaint);
            }
        }
        
        @Override
        public void onClick(View v) {
            switch (mCurrentState) {
            case PRE_START:
                if (listener != null) {
                    listener.onStart();
                }
                mCurrentState = ProgressState.RUNNING;
                break;
            case RUNNING:
                if (listener != null) {
                    listener.onPause(mProgress * 100 / mMax);
                }
                mCurrentState = ProgressState.PAUSE;
                invalidate();
                break;
            case PAUSE:
                if (listener != null) {
                    listener.onStart();
                }
                mCurrentState = ProgressState.RUNNING;
                invalidate();
                break;
            case COMPLETELY:
                if (listener!=null) {
                    listener.onCompletedClick();
                }
                break;
            }
        }
        
        private OnProgressClickListener listener;
        
        /**
         * 设置最大值
         * @param max 最大值
         */
        public void setMax(int max){
            mMax=max;
        }
        
        /**
         * 设置当前进度
         * @param progress 当前进度
         */
        public void setProgress(int progress){
            mProgress=progress;
            if (mCurrentState!=ProgressState.RUNNING) {
                mCurrentState=ProgressState.RUNNING;
            }
            if (mProgress>=mMax) {
                mCurrentState=ProgressState.COMPLETELY;
                if (listener!=null) {//进度圆完成回调
                    listener.onCompletely();
                }
            }
            invalidate();
        }
    
        /**
         * 设置监听事件
         * @param l 监听器
         */
        public void setOnProgressClickListener(OnProgressClickListener l) {
            this.listener = l;
        }
        
        /**
         * 这里提供了四个回调方法,比较多,可能只用到其中几个,
         * 所以采用了抽象类来实现,除了必要的开始操作以外,
         * 其他的操作用户需要哪个方法自己复写就行了。 
         */
        public static abstract class OnProgressClickListener {
            /** 开始 */
            public abstract void onStart();
    
            /** 暂停 */
            public void onPause(int percent){};
    
            /** 结束 */
            public void onCompletely(){};
            
            /** 完成后点击 */
            public void onCompletedClick(){};
        }
        
        /**控件状态*/
        public enum ProgressState{
            /**开始之前*/
            PRE_START,
            /**运行*/
            RUNNING,
            /**暂停*/
            PAUSE,
            /**完成*/
            COMPLETELY;
        }
        
    }

    接下来我们说说怎么使用,现在xml中定义我们的自定义控件。

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context="${relativePackage}.${activityClass}"
        android:background="@android:color/black" >
    
        <com.lwd.playbutton.PlayButton
            android:id="@+id/buffer_button"
            android:layout_width="50dp"
            android:layout_height="50dp"
            />
    
    </RelativeLayout>

    然后我们在Activity中模拟一下缓冲视频,并且播放的操作。

    package com.lwd.playbutton;
        
    import com.lwd.bufferbutton.R;
    import com.lwd.playbutton.PlayButton.OnProgressClickListener;
    
    import android.app.Activity;
    import android.os.Bundle;
    import android.os.SystemClock;
    import android.widget.Toast;
    /**
     * 模拟视频缓冲的activity
     * @author Vitor Lee
     */
    public class MainActivity extends Activity {
    
        private static final int DEFAULT_MAX_VALUE = 100;
        private int mProgress = 0;
        private PlayButton mProgressView;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            mProgressView = (PlayButton) findViewById(R.id.buffer_button);
            mProgressView.setOnProgressClickListener(getProgressClickListener());
            mProgressView.setMax(DEFAULT_MAX_VALUE);
        }
    
        private OnProgressClickListener getProgressClickListener() {
            return new OnProgressClickListener() {
                
                private Thread mDownloadThread;
                private boolean isStop;
                
                @Override
                public void onStart() {//模拟下载
                    if (mDownloadThread==null) {
                        mDownloadThread = new Thread() {
                            @Override
                            public void run() {
                                while (true) {
                                    if (!isStop) {
                                        runOnUiThread(new Runnable() {
                                            @Override
                                            public void run() {
                                                mProgressView.setProgress(mProgress);
                                                mProgress++;
                                            }
                                        });
                                        if (mProgress==DEFAULT_MAX_VALUE) {
                                            break;
                                        }
                                    }
                                    SystemClock.sleep(100);
                                }
                            }
                        };
                        mDownloadThread.start();
                    }
                    isStop=false;
                }
    
                @Override
                public void onPause(int percent) {//暂停
                    isStop=true;
                }
    
                @Override
                public void onCompletely() {
                    Toast.makeText(MainActivity.this, "完成", 1).show();
                }
    
                @Override
                public void onCompletedClick() {//缓冲完成之后点击播放
                    Toast.makeText(MainActivity.this, "播放", 1).show();
                }
                
            };
        }
    }
  • 相关阅读:
    Sprng Data JPA与hibernate的关系
    Exception in thread "main" java.io.FileNotFoundException: d:xxx.txt (拒绝访问。)
    Telnet测试BIO Socket
    计算机网络篇
    Postman测试接口发现时间少8个小时?添加这两行代码就解决!
    匿名内部类
    解决线程安全的三种方法
    equals和hashcode的区别
    postman测试springsecurity 登录鉴权,获取Cookie后进行其他接口测试
    (链表)求相交链表交点
  • 原文地址:https://www.cnblogs.com/zhujiabin/p/5891545.html
Copyright © 2020-2023  润新知