自定义View是android开发的一个重要技能,用android提供的2/3D绘制相关类可以实现非常多炫酷的效果,需要实打实的编程基础。(吧)。

但是自定义View又是我的弱项,所以最近都在摸索、练习自定义View。今天我写了一个圆形图片,同时不断匀速旋转的RotateCircleImageView。实现方法是自己想的,但肯定不是最好的实现方法。

自定义View分四步。

一:自定义属性;

二:创建自定义View,在构造方法中拿到自定义属性;

三:重写onMeasure方法;

四:重写onDraw方法

先来个效果图

先在res/values/下新建attrs.xml

自定义属性

<declare-styleable name="RotateCircleImageView"><attr name="image" format="reference" /><attr name="rotate_sd" format="float" /><attr name="rotate_fx" format="integer" /><attr name="isRotate" format="boolean" /><attr name="circle_back_width" format="dimension" /><attr name="circle_back_color" format="color" /></declare-styleable>

创建RotateCircleImageView

public RotateCircleImageView(Context context) {this(context, null);}public RotateCircleImageView(Context context, AttributeSet attrs) {this(context, attrs, 0);}public RotateCircleImageView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);initData();}

重写View的三个构造函数,用一参的调用二参的,用二参的调用三参的。在三参的构造里初始化参数。

private Bitmap image;
private Bitmap tempImage;
private Paint paint;
private int bkWidth;//黑色圆边框的宽度
private int rotate_fx=0;//旋转方向  0=顺时针 1=逆时针
private float rotateSD = 0.8f;//每次旋转的角度--建议范围0.1f-1,否则会抖动
private boolean isRotate = false;//控制是否旋转private void initData() {paint = new Paint();paint.setAntiAlias(true);paint.setDither(true);TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs,R.styleable.RotateCircleImageView, defStyleAttr, 0);//用这个类获得自定义的属性paint.setColor(typedArray.getColor(R.styleable.RotateCircleImageView_circle_back_color,Color.BLACK));tempImage = BitmapFactory.decodeResource(getResources(), typedArray.getResourceId(R.styleable.RotateCircleImageView_image, R.mipmap.ic_launcher));bkWidth = typedArray.getDimensionPixelSize(R.styleable.RotateCircleImageView_circle_back_width,DensityUtils.dp2px(context, 100));//黑色边框的宽度,DensityUtils是我的一个工具类,将dp转换成px的rotateSD = typedArray.getFloat(R.styleable.RotateCircleImageView_rotate_sd, 0.8f);rotate_fx = typedArray.getInt(R.styleable.RotateCircleImageView_rotate_fx, 0);isRotate = typedArray.getBoolean(R.styleable.RotateCircleImageView_isRotate, true);
}

重写测量方法:主要是测量包裹内容的情况下宽度和高度的值

@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);int widthMode = MeasureSpec.getMode(widthMeasureSpec);int widthSize = MeasureSpec.getSize(widthMeasureSpec);int heightMode = MeasureSpec.getMode(heightMeasureSpec);int heightSize = MeasureSpec.getSize(heightMeasureSpec);//分别拿到宽高的大小和测量模式int mWidth;//最终宽度int mHeight;//最终高度int yy_width = widthSize;//预测宽度,先假设它等于指定大小或填充窗体if (widthMode == MeasureSpec.EXACTLY) {mWidth = widthSize;//如果是指定大小或填充窗体(以后直接说成指定大小),直接设置最终宽度} else {yy_width=tempImage.getWidth();//如果是包裹内容,则预测宽度等于图片宽度mWidth = yy_width + getPaddingLeft() + getPaddingRight();//最终宽度等于预测宽度加 左右Padding宽度}if (heightMode == MeasureSpec.EXACTLY) {mHeight = heightSize;//同上} else {mHeight = getPaddingTop() + getPaddingBottom() + yy_width;//最终高度等于预测宽度加 上下Padding宽度//目的是让控件的宽高相等,但Padding是可以由用户自由指定的,所以再加上padding}if (tempImage.getHeight() < tempImage.getWidth()) {//这里用Bitmap类提供的缩放方法把图片缩放成指定大小,如果图片高度比宽度小,则强制拉伸image = Bitmap.createScaledBitmap(tempImage, yy_width - bkWidth,yy_width - bkWidth, false);} else {//这里用Bitmap类提供的缩放方法把图片缩放成指定大小(宽度等于预测的宽度,高度按比例缩放)//该方法根据参数的宽高强制缩放图片,所以这里根据宽度算出缩放后的高度image = Bitmap.createScaledBitmap(tempImage, yy_width - bkWidth,(int) (tempImage.getHeight() /(((float) tempImage.getWidth()) / yy_width) - bkWidth), false);}setMeasuredDimension(mWidth, mHeight);//设置View的宽高,测量结束}

假如宽度是指定大小,我希望高度根据这个大小按比例缩放,那么我需要拿到图片原始大小,所以需要一个tempImage,为什么写一个临时的Bitmap?因为我测试的时候发现   假如我用这个image直接把Bitmap.createScaledBitmap(image,xx,xx,false);的返回值赋给image的话,即使我在这行代码前去用image.getWidth()和Image.getHeight(),返回的值都已经变成缩放后的大小,而不是原始大小,这让我感到很奇怪。难道BItmap的getWidth和getHeight是异步的吗?希望知道的人帮我解答。

最后重写onDraw方法

@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);canvas.drawCircle(getWidth() / 2, getWidth() / 2 , getWidth() / 2, paint);//绘制黑色圆canvas.drawBitmap(getCircleBitmap(image, image.getWidth(), rotateSD),getWidth() / 2 - image.getWidth() / 2,getHeight() / 2 - image.getWidth() / 2, paint);//绘制圆形图片if (isRotate) {handler.postDelayed(runnable, 16);//16毫秒后启动子线程}}

getCircleBitmap方法和子线程的代码:

 private Bitmap bitmap;private boolean isCreateBitmap = false;private Canvas canvas;private PorterDuffXfermode pdf;private Paint bitmapPaint;private Bitmap getCircleBitmap(Bitmap image, int width, float rotate) {if (!isCreateBitmap) {//节约资源所以这些代码只需要执行一次bitmapPaint = new Paint();bitmapPaint.setAntiAlias(true);//抗锯齿bitmapPaint.setDither(true);//忘了是啥....反正效果好点bitmap = Bitmap.createBitmap(width, width, Bitmap.Config.ARGB_8888);//创建一个指定宽高的空白bitmapisCreateBitmap = true;canvas = new Canvas(bitmap);//用那个空白bitmap创建一个画布canvas.drawCircle(width / 2, width / 2, width / 2, bitmapPaint);//在画布上画个圆pdf = new PorterDuffXfermode(PorterDuff.Mode.SRC_IN);//创建一个混合模式为保留后者相交的部分}bitmapPaint.setXfermode(pdf);//设置混合模式if (rotate_fx==0) {canvas.rotate(rotate, width / 2, width / 2);//顺时针} else {//旋转画布:意思是下一次绘制的内容会被旋转这么多个角度canvas.rotate(-rotate, width / 2, width / 2);//逆时针}canvas.drawBitmap(image, 0, 0, bitmapPaint);//绘制图片,(图片会被旋转)bitmapPaint.setXfermode(null);return bitmap;//这个bitmap在画布中被旋转,画圆,返回后就是一个圆形的bitmap}private Handler handler = new Handler();private Runnable runnable = new Runnable() {@Overridepublic void run() {invalidate();//刷新界面}};

在第一次执行onDraw方法的时候得到的是一个旋转了0.8度的bitmap,然后16毫秒后启动子线程刷新,再次执行onDraw,得到一个再次旋转0.8度的bitmap,以此类推,所以不断旋转。想要转的快一点就把每次旋转的角度调大一点,但是不能太大,否则效果很不好。一卡一卡的。这样就完成了这个自定义view,非常简单,但是我却折腾了好久,主要还是测量的时候不够细心。实现方法都是自己整出来的,如果有更好的实现方法欢迎告知。

最后再暴露两个方法给外部

 public void startRotate() {//开始旋转if (!isRotate) {this.isRotate = true;invalidate();}}public void stopRotate() {//暂停旋转isRotate = false;}

然后可以在布局里试试了:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"android:layout_width="match_parent"android:layout_height="match_parent"android:fitsSystemWindows="true"android:orientation="vertical"><com.as.liji.jishiben.view.RotateCircleImageViewandroid:id="@+id/rcv"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_centerInParent="true"app:circle_back_width="80dp"app:image="@mipmap/sm"app:isRotate="false"app:rotate_fx="0"app:rotate_sd="0.5" /><TextViewandroid:id="@+id/tv"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_below="@id/rcv"android:layout_centerHorizontal="true"android:ellipsize="marquee"android:text="正在播放:蜘蛛侠插曲--Hold On" /><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_below="@id/tv"android:orientation="horizontal"><Buttonandroid:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:onClick="startRotate"android:text="开始" /><Buttonandroid:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:onClick="stopRotate"android:text="暂停" /></LinearLayout></RelativeLayout>

在activity中拿到控件,重写两个按钮的点击事件方法:

private RotateCircleImageView rcv;........onCreate(){
........
rcv = (RotateCircleImageView) findViewById(R.id.rcv);
}
public void startRotate(View v) {rcv.startRotate();}public void stopRotate(View v) {rcv.stopRotate();}

---------------------END---------------------

Android自定义View实现不断旋转的圆形图片相关推荐

  1. Android自定义view,圆形的TextView,并通过xml设置属性,AttributeSet中取值

    Android自定义view设置xml属性 一个圆形的自定义TextView,通过xml来设置背景颜色的属性 values/attrs <declare-styleable name=" ...

  2. android+清除循环动画,android自定义View之(4)-一键清除动画

    android自定义View之(四)------一键清除动画 1.前言: 自己也是参考别人的一些自定义view例子,学习了一些基本的自定义view的方法.今天,我参考了一些资料,再结合自已的一些理解, ...

  3. android自定义View之(四)------一键清除动画

    1.前言: 自己也是参考别人的一些自定义view例子,学习了一些基本的自定义view的方法.今天,我参考了一些资料,再结合自已的一些理解,做了一个一键清除的动画.当年,我实现这个是用了几张图片,采用F ...

  4. Android自定义View之画圆环(进阶篇:圆形进度条)

    前言: 如果你想读懂或者更好的理解本篇文章关于自定义圆环或圆弧的内容.请你务必提前阅读下Android自定义View之画圆环(手把手教你如何一步步画圆环).在这篇文章中,详细描述了最基本的自定义圆环的 ...

  5. Android 自定义View 圆形圆角图片

    [Android 自定义View 圆形圆角图片] 基于Xfermode 实现 1.概述 在很久以前也写过一个利用Xfermode 实现圆形.圆角图片的(Android 完美实现图片圆角和圆形(对实现进 ...

  6. android 圆环温度控件,Android自定义View分享——一个圆形温度显示器

    写在前面 笔者近来在学习Android自定义View,收集了一些不算复杂但又"长得"还可以的自定义View效果实现,之前分享过一个水平的进度条,如果你有兴趣的话可以看看: Andr ...

  7. android view 渐变动画,Android自定义view渐变圆形动画

    本文实例为大家分享了Android自定义view渐变圆形动画的具体代码,供大家参考,具体内容如下 直接上效果图 自定义属性 attrs.xml文件 创建一个类 ProgressRing继承自 view ...

  8. 精通Android自定义View(十二)绘制圆形进度条

    1 绘图基础简析 1 精通Android自定义View(一)View的绘制流程简述 2 精通Android自定义View(二)View绘制三部曲 3 精通Android自定义View(三)View绘制 ...

  9. android自定义View之(六)------高仿华为荣耀3C的圆形刻度比例图(ShowPercentView)

    为什么写这篇文章: 显示当前的容量所占的比例,表现当前计划的进度,一般都会采用百分比的方式,而图形显示,以其一目了然的直观性和赏心悦目的美观形成为了我们的当然的首选. 在图形表示百分比的方法中,我们有 ...

最新文章

  1. Go语言写的解析器(支持json,linq,sql,net,http等)
  2. cannot import name filter
  3. matlab一句一句调试,matlab程序怎样调试和运行
  4. 第四周实践项目6 循环双链表应用
  5. HTML+CSS+JavaScript复习笔记持更(四)——多媒体篇
  6. SpringCloud实战4-Hystrix线程隔离请求缓存请求合并
  7. 如何删除Win All的流氓程序文件
  8. (日常搬砖)windows下如何查看并导出文件夹目录
  9. IT忍者神龟之Java动态代理与CGLib代理
  10. 英特尔12代酷睿处理器强势来袭
  11. 【有限差分法】(一)有限差分法的基本流程与常用格式
  12. CMPSS 涡扇发动机模型
  13. Kotlin中let、also、with、run和apply使用
  14. 网络流量分析工具(tcpdump)
  15. (超详细) eclispe如何连接SQL Server2019(通过JDBC驱动包连接)
  16. 多宽带联网(二) OpenWrt中利用MWAN3负载均衡实现带宽叠加
  17. zookeeper-集群-选举机制
  18. 如何利用VSCode书写Latex并进行编译
  19. java lang arithmetic,java.lang.ArithmeticException
  20. 网站倒计时使用服务器时间,根据服务器时间校准倒计时时间

热门文章

  1. HP Elitebook 8440p解决ubuntu10.10显卡,无线网卡不能用
  2. java计算机毕业设计美食推荐管理系统源程序+mysql+系统+lw文档+远程调试
  3. 装饰器模式,圣诞节的礼物
  4. 6-1 邻接矩阵存储图的深度优先遍历 (20 分)(C语言版)
  5. 集“XXXXX”或它的某一个依赖项。生成此程序集的运行时比当前加载的运行时新,无法加载此程序集。”
  6. java多态计算几何,通过程序设计几何图形、矩形、圆形、正方形、几种类型,能过利用接口和多态性计算几何图形的面积和周长并显示出来...
  7. 数据库分页查询PageHelper
  8. http状态码批量检测工具
  9. log4j实现日志记录
  10. 用户态和内核态的简单理解