(Values下)attrs.xml

[html] view plain copy
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <resources>
  3. <declare-styleable name="GramophoneView">
  4. <attr name="picture_radiu" format="dimension"/>            //中间图片的半径
  5. <attr name="src" format="reference"/>                //图片
  6. <attr name="disk_rotate_speed" format="float"/>     //唱片旋转的速度
  7. </declare-styleable>
  8. </resources>

自定义View

[java] view plain copy
  1. public class GramophoneView extends View {
  2. /**
  3. * 尺寸计算设计说明:
  4. * 1、唱片有两个主要尺寸:中间图片的半径、黑色圆环的宽度。
  5. * 黑色圆环的宽度 = 图片半径的一半。
  6. * 2、唱针分为“手臂”和“头”,手臂分两段,一段长的一段短的,头也是一段长的一段短的。
  7. * 唱针四个部分的尺寸求和 = 唱片中间图片的半径+黑色圆环的宽度
  8. * 唱针各部分长度 比例——长的手臂:短的手臂:长的头:短的头 = 8:4:2:1
  9. * 3、唱片黑色圆环顶部到唱针顶端的距离 = 唱针长的手臂的长。度
  10. */
  11. private final float DEFUALT_DISK_ROTATE_SPEED = 1f;      //磁盘旋转的速度
  12. private final float DEFUALT_PICTURE_RAUID = 200;         //中间图片默认半径
  13. private final float DEFUALT_PAUSE_NEEDLE_DEGREE = -45;  //暂停状态时唱针的旋转角度
  14. private final float DEFUALT_PLAYING_NEEDLE_DEGREE = -15;     //播放状态时唱针的旋转角度
  15. private int pictrueRadio;   //中间图片的半径
  16. //指针
  17. private int smallCircleRadiu = 10;  //唱针顶部小圆半径,减小了一半
  18. private int bigCircleRadiu = 15;    //唱针顶部大圆半径,减小了一半
  19. private int shortArmLength;
  20. private int longArmleLength;         // 唱针手臂,较长那段的长度
  21. private int shortHeadLength;         // 唱针的头,较短那段的长度
  22. private int longHeadLength;
  23. private Paint needlePaint;
  24. //唱片
  25. private float halfMeasureWidth;
  26. private int diskRingWidth;            // 黑色圆环宽度
  27. private float diskRotateSpeed;        // 唱片旋转速度
  28. private Bitmap pictureBitmap;
  29. private Paint diskPaint;
  30. //状态控制
  31. private boolean isPlaying;
  32. private float currentDiskDegree;            // 唱片旋转角度
  33. private float currentNeddleDegree = DEFUALT_PLAYING_NEEDLE_DEGREE;  // 唱针旋转角度
  34. public GramophoneView(Context context) {
  35. this(context,null);
  36. }
  37. public GramophoneView(Context context, @Nullable AttributeSet attrs) {
  38. super(context, attrs);
  39. needlePaint = new Paint(Paint.ANTI_ALIAS_FLAG);//抗锯齿
  40. diskPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
  41. TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.GramophoneView);
  42. //        拿到xml中的图片和图片半径和,旋转的度数
  43. pictrueRadio = (int) typedArray.getDimension(R.styleable.GramophoneView_picture_radiu, DEFUALT_PICTURE_RAUID);
  44. diskRotateSpeed = typedArray.getFloat(R.styleable.GramophoneView_disk_rotate_speed, DEFUALT_DISK_ROTATE_SPEED);
  45. Drawable drawable = typedArray.getDrawable(R.styleable.GramophoneView_src);
  46. if (drawable == null) {
  47. pictureBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);
  48. } else {
  49. pictureBitmap = ((BitmapDrawable)drawable).getBitmap();
  50. }
  51. //初始化唱片的变量
  52. diskRingWidth = pictrueRadio >> 1;
  53. shortHeadLength = (pictrueRadio + diskRingWidth) / 15;    //图片半径和黑色圆环的和 等于 指针的总长度
  54. longHeadLength = shortHeadLength << 1;    //左移相当于乘以2
  55. shortArmLength = longHeadLength << 1;
  56. longArmleLength = shortArmLength << 1;
  57. }
  58. /**
  59. * 理想的宽高是,取决于picture的 半径的
  60. *
  61. * @param widthMeasureSpec
  62. * @param heightMeasureSpec
  63. */
  64. @Override
  65. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  66. super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  67. //我们想要的理想宽高
  68. int width = (pictrueRadio+diskRingWidth)*2;
  69. int hight = (pictrueRadio+diskRingWidth)*2+longArmleLength;
  70. //根据我们理想的宽和高 和xml中设置的宽高,按resolveSize规则做最后的取舍
  71. //resolveSize规则 1、精确模式,按
  72. int measurewidth = resolveSize(width,widthMeasureSpec);
  73. int measurehight = resolveSize(hight,heightMeasureSpec);
  74. setMeasuredDimension(measurewidth,measurehight);//设置测量的长度
  75. }
  76. @Override
  77. protected void onDraw(Canvas canvas) {
  78. super.onDraw(canvas);
  79. halfMeasureWidth = getMeasuredWidth() >> 1;
  80. drawDisk(canvas);   //画唱片
  81. drawNeedle(canvas);//画指针
  82. if (currentNeddleDegree > DEFUALT_PAUSE_NEEDLE_DEGREE) {
  83. invalidate();
  84. }
  85. }
  86. private void drawNeedle(Canvas canvas) {//指针
  87. canvas.save();//保存
  88. //移动坐标原点,画指针第一段
  89. canvas.translate(halfMeasureWidth, 0);
  90. canvas.rotate(currentNeddleDegree);
  91. needlePaint.setColor(Color.parseColor("#C0C0C0"));
  92. needlePaint.setStrokeWidth(10);
  93. canvas.drawLine(0, 0, 0, longArmleLength, needlePaint);
  94. //画指针第二段
  95. canvas.translate(0, longArmleLength);
  96. canvas.rotate(-30);//
  97. needlePaint.setStrokeWidth(10);
  98. canvas.drawLine(0, 0, 0, shortArmLength, needlePaint);
  99. //画指针第三段
  100. canvas.translate(0, shortArmLength);
  101. needlePaint.setStrokeWidth(15);
  102. canvas.drawLine(0, 0, 0, longHeadLength, needlePaint);
  103. //画指针的第四段
  104. canvas.translate(0, longHeadLength);
  105. needlePaint.setStrokeWidth(25);
  106. canvas.drawLine(0, 0, 0, shortHeadLength, needlePaint);
  107. canvas.restore();
  108. //画指针的支点
  109. canvas.save();
  110. canvas.translate(halfMeasureWidth, 0);
  111. needlePaint.setColor(Color.parseColor("#8A8A8A"));
  112. needlePaint.setStyle(Paint.Style.FILL);
  113. canvas.drawCircle(0, 0, bigCircleRadiu, needlePaint);
  114. needlePaint.setColor(Color.parseColor("#C0C0C0"));
  115. canvas.drawCircle(0, 0, smallCircleRadiu, needlePaint);
  116. canvas.restore();
  117. //当前如果是播放的话,就移动到播放的位置 ,因为逆时针旋转度数是负的所以,-  + 需要注意
  118. if (isPlaying) {
  119. if (currentNeddleDegree < DEFUALT_PLAYING_NEEDLE_DEGREE) {  //不是暂停状态,就是播放状态,或者是切换中状态
  120. currentNeddleDegree += 3;  //切换中状态指针是要有动画效果的,所有要改变指针的度数
  121. }
  122. } else {
  123. if (currentNeddleDegree > DEFUALT_PAUSE_NEEDLE_DEGREE) {
  124. currentNeddleDegree -= 3;
  125. }
  126. }
  127. }
  128. private void drawDisk(Canvas canvas) {
  129. currentDiskDegree = currentDiskDegree % 360 + diskRotateSpeed;
  130. canvas.save();
  131. canvas.translate(halfMeasureWidth, longArmleLength + diskRingWidth + pictrueRadio);
  132. canvas.rotate(currentDiskDegree);
  133. diskPaint.setColor(Color.BLACK);
  134. diskPaint.setStyle(Paint.Style.STROKE);
  135. diskPaint.setStrokeWidth(pictrueRadio / 2);
  136. //        diskPaint.setStrokeWidth(20);
  137. canvas.drawCircle(0, 0, pictrueRadio + diskRingWidth / 2, diskPaint);
  138. Path path = new Path();       // 裁剪的path路径 (为了裁剪成圆形图片,其实是将画布剪裁成了圆形)
  139. path.addCircle(0, 0, pictrueRadio, Path.Direction.CW);
  140. canvas.clipPath(path);
  141. Rect src = new Rect();                  //将要画bitmap的那个范围
  142. src.set(0, 0, pictureBitmap.getWidth(), pictureBitmap.getHeight());
  143. Rect dst = new Rect();
  144. dst.set(-pictrueRadio, -pictrueRadio, pictrueRadio, pictrueRadio);      //将要将bitmap画要坐标系的那个位置
  145. canvas.drawBitmap(pictureBitmap, src, dst, null);
  146. canvas.restore();
  147. }
  148. public void pauseOrstart(){
  149. isPlaying =!isPlaying;
  150. invalidate();
  151. }
  152. /**
  153. * 设置图片半径
  154. *
  155. * @param pictureRadius 图片半径
  156. */
  157. public void setPictureRadius(int pictureRadius) {
  158. this.pictrueRadio = pictureRadius;
  159. }
  160. /**
  161. * 设置唱片旋转速度
  162. *
  163. * @param diskRotateSpeed 旋转速度
  164. */
  165. public void setDiskRotateSpeed(float diskRotateSpeed) {
  166. this.diskRotateSpeed = diskRotateSpeed;
  167. }
  168. /**
  169. * 设置图片资源id
  170. *
  171. * @param resId 图片资源id
  172. */
  173. public void setPictureRes(int resId) {
  174. pictureBitmap = BitmapFactory.decodeResource(getContext().getResources(), resId);
  175. invalidate();
  176. }
  177. }

mainActivity.xml

[html] view plain copy
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. xmlns:app="http://schemas.android.com/apk/res-auto"
  4. xmlns:tools="http://schemas.android.com/tools"
  5. android:layout_width="match_parent"
  6. android:layout_height="match_parent"
  7. android:orientation="vertical"
  8. tools:context="com.example.jinmuyan.phonograph.MainActivity">
  9. <com.example.jinmuyan.phonograph.GramophoneView
  10. android:id="@+id/gramophone"
  11. app:src="@drawable/lang"
  12. app:picture_radiu="80dp"
  13. app:disk_rotate_speed="1"
  14. android:layout_width="wrap_content"
  15. android:layout_height="wrap_content"
  16. />
  17. <Button
  18. android:layout_marginTop="60dp"
  19. android:onClick="pauseOrstart"
  20. android:text="切换播放状态"
  21. android:layout_width="wrap_content"
  22. android:layout_height="wrap_content" />
  23. </LinearLayout>

MainActivity

[java] view plain copy
  1. public class MainActivity extends AppCompatActivity {
  2. private GramophoneView gramophoneView;
  3. @Override
  4. protected void onCreate(Bundle savedInstanceState) {
  5. super.onCreate(savedInstanceState);
  6. setContentView(R.layout.activity_main);
  7. gramophoneView = (GramophoneView) findViewById(R.id.gramophone);
  8. }
  9. public void pauseOrstart(View view) {
  10. gramophoneView.pauseOrstart();
  11. }
  12. }

自定义View_留声机效果相关推荐

  1. Flutter RichText支持自定义文本溢出效果

    extended text 相关文章 Flutter RichText支持图片显示和自定义图片效果 Flutter RichText支持自定义文本溢出效果 Flutter RichText支持自定义文 ...

  2. android 自定义刷新控件,Android开发中MJRefresh自定义刷新动画效果

    有时候我们对自己开发的项目经常不满意,但是我们要达到自定义刷新动画的效果有一定的难度,别着急,下面爱站技术频道和大家分享Android开发中MJRefresh自定义刷新动画效果,一起来学习吧! [一] ...

  3. Android自定义类似ProgressDialog效果的Dialog

    2019独角兽企业重金招聘Python工程师标准>>> Android自定义类似ProgressDialog效果的Dialog. 方法如下: 1.首先准备两张自己要定义成哪样子的效果 ...

  4. Android使用xml自定义软键盘效果(附源码)

    Android使用xml自定义软键盘效果原理: 1,软键盘其实是个控件,使用android.inputmethodserver.KeyboardView类定义. 2,主布局中使用帧布局,当我们需要显示 ...

  5. android自定义控件颜色渐变,Android编程实现自定义渐变颜色效果详解

    本文实例讲述了Android编程实现自定义渐变颜色效果.分享给大家供大家参考,具体如下: 你是否已经厌恶了纯色的背景呢?那好,Android提供给程序员自定义渐变颜色的接口,让我们的界面炫起来吧. x ...

  6. [Android]自定义View带效果的滚动数字

    [Android]自定义View带效果的滚动数字 @Author GQ 2016年07月29日 一个可以让数字滚动的View,可以自定义参数,是想要的那种效果! 原文github地址 效果图 Andr ...

  7. Unity 实现自定义图片破碎效果-2D_Destruction

    Unity 实现自定义图片破碎效果-2D_Destruction 导引 效果预览 源码下载地址 实现流程 1.添加SrpiteRenderer组件 2.添加Explodable组件 3.Polygon ...

  8. R语言ggplot2可视化:通过在element_text函数中设置标签字体大小列表和标签字体形式列表自定义标签可视化效果

    R语言ggplot2可视化:通过在element_text函数中设置标签字体大小列表和标签字体形式列表自定义标签可视化效果 目录

  9. html如何自定义一个动画效果,30个纯css动画代码片段和效果演示

    开源代码已经迎来了一个新的web前端时代.初学者和专家都可以通过使用预构建的代码片段节省时间和压力.下面画廊由30个不同的片段与纯CSS创建动画效果. 所有这些代码片段都放在云IDE平台.这些weba ...

最新文章

  1. Python 读取文本时的 read/readline/readlines
  2. python pyside简单布局_python – pyside显示/隐藏布局
  3. 解决问题:EnvironmentLocationNotFound: Not a conda environment: /anaconda3/envs/anaconda3
  4. Centos7安装netstat及简单使用
  5. halcon edges_sub_pix算子
  6. 栈和队列之设计一个有getMin(得到最小值)功能的栈
  7. POE交换机产品如何设计防雷保护?
  8. [Redux/Mobx] 举例说明怎么在redux中定义action?
  9. thinkphp Hook行为的使用案例
  10. ebook_7种开放式eBook格式指南
  11. 【华为云技术分享】在 K8S 大规模场景下 Service 性能如何优化?
  12. java.util.concurrent.RejectedExecutionException: event executor terminated 错误分析
  13. unix 网络编程总结
  14. STL--queue
  15. 小白Linux入门之:终端复用器Tmux使用参考
  16. python设计查询余额程序_使用Python调取任意数字资产钱包余额功能
  17. c++操作打印机那些事
  18. 第五章:项目范围管理 - (5.4 创建 WBS )
  19. MATLAB | 好看的配对箱线图绘制模板
  20. 理解Spring 容器设计理念

热门文章

  1. 网络故障维修顺序步骤
  2. JSTA:将空间转录组原始数据转换成单细胞水平空间表达谱的专用工具
  3. katakana.php,半角片假名 全角片假名转换器 - Hi!Penpal!
  4. C++ 类的成员函数
  5. 万豪旅享家“双十一”惊喜大促于飞猪旅行火热开启;山东山印格兰云天大酒店正式开业 | 全球旅报...
  6. 程序员怎样提高项目管理意识
  7. 解决steam vr游戏 aircar 进入游戏只能前进和后退的问题
  8. 会生活会编程——我的极简主义尝试
  9. IT职业培训尝试突围-东方标准发布《中国IT从业人员心理特征研究报告》
  10. Easy Paint Tool SAI 2.0+62种笔刷 超级好用的画图软件