Android 自定义View实现QQ运动积分抽奖转盘

  因为偶尔关注QQ运动, 看到QQ运动的积分抽奖界面比较有意思,所以就尝试用自定义View实现了下,原本想通过开发者选项查看下界面的一些信息,后来发现积分抽奖界面是在WebView中展示的,应该是在H5页面中用js代码实现的,暂时不去管它了。

  这里的自定义View针对的是继承自View的情况,你可以将Canvas想象为画板, Paint为画笔,自定义View的过程和在画板上用画笔作画其实类似,想象在画板上作画的过程,你要画一个多大图形(对应View的测量 onMeasure方法),你要画什么样的图形,比如圆形方形等等(对应View的onDraw方法),在掌握了View的一些基础概念(位置参数、触摸事件、滑动),测量模式、事件分发机制、绘制流程等知识后,自定义View的时候就不觉得复杂了。

  不管是多么复杂的View,其内部基本都可以拆分至一个个小单元,比如如下的QQ运动积分抽奖画面,(QQ --> 动态 --> 运动 --> 我 --> 积分)

  

 

   这里我们只关注抽奖的转盘,因为是截图没有动画效果,具体可以在自己的手机上查看下。这个抽奖的界面看似复杂,其实可以分为几个部分

  1. 最外层圆环,其中有小圆圈闪动

  2, 内部圆角矩形

  3.  内部圆角卡片(包含一个图片或说明文字)

 

   第一步我们要继承View类, 如果需要自定义属性则应该实现带三个参数的构造方法,这里将自定义View命名为 LotteryView

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

  在init方法中可以做一些初始化的操作,比如需要用的颜色值,画笔Paint, 宽高信息等,如果有自定义属性,也可以在init方法中处理。

  接着可以设定View的宽高信息,这里我们将View设置为正方形

  

 @Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {//super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        setMeasuredDimension(mSelfTotalWidth, mSelfTotalWidth);}

  调用setMeasureDimension方法,宽高都设置为 mSelfTotalWidth。这里我们宽高是限定的值,所以不需要的处理不同测量模式的情况,如果是其他自定义View要支持wrap_content属性,需要在onMeasure方法中自行处理

  第一步 :绘制外层带圆角的圆环

  

/** 外层带圆角矩形圆环 */private void drawOuterRoundCircle(Canvas canvas) {canvas.save();canvas.clipRect(mOuterCircleWidth + getPaddingLeft(),mOuterCircleWidth + getPaddingTop(),mSelfTotalWidth - mOuterCircleWidth - getPaddingRight(),mSelfTotalWidth - mOuterCircleWidth - getPaddingBottom(),Region.Op.DIFFERENCE);canvas.drawRoundRect(getPaddingLeft(),getPaddingTop(),mSelfTotalWidth - getPaddingRight(),mSelfTotalWidth - getPaddingBottom(),18, 18, mOuterCirclePaint);canvas.restore();}

  

  绘制外层圆环中的小圆圈

private void drawOuterDecorateSmallCircle(Canvas canvas) {int result = mInvalidateCircleCount % 2;// topint x = 0, y = 0;int sideSize = mSelfTotalWidth - mOuterCircleWidth * 2 - getPaddingLeft() - getPaddingRight(); // 除去最外边圆环后的边长for (int i = 0; i < 10; i++) {mSmallCirclePaint.setColor(i % 2 == result ? mSmallCircleYellowColor : mSmallCircleBlueColor);x = mOuterCircleWidth + (sideSize - mSmallCircleRadius * 2 * 9) / 9 * i + mSmallCircleRadius * 2 * i + getPaddingLeft();y = (mOuterCircleWidth - mSmallCircleRadius * 2) / 2 + mSmallCircleRadius + getPaddingTop();canvas.drawCircle(x, y, mSmallCircleRadius, mSmallCirclePaint);}// bottomfor (int i = 0; i < 10; i++) {mSmallCirclePaint.setColor(i % 2 == result ? mSmallCircleYellowColor : mSmallCircleBlueColor);x = mOuterCircleWidth + (sideSize - mSmallCircleRadius * 2 * 9) / 9 * i + mSmallCircleRadius * 2 * i + getPaddingLeft();y = mSelfTotalWidth - mOuterCircleWidth + (mOuterCircleWidth - mSmallCircleRadius * 2) / 2 + mSmallCircleRadius - getPaddingBottom();canvas.drawCircle(x, y, mSmallCircleRadius, mSmallCirclePaint);}// leftfor(int i = 0; i < 9; i++) {mSmallCirclePaint.setColor(i % 2 == (result == 0 ? 1 : 0) ? mSmallCircleYellowColor : mSmallCircleBlueColor);x = mOuterCircleWidth / 2 + getPaddingLeft();y =  mOuterCircleWidth*2 + (sideSize - mSmallCircleRadius * 2 * 9) / 9 * i + mSmallCircleRadius * 2 * i + getPaddingTop();canvas.drawCircle(x, y, mSmallCircleRadius, mSmallCirclePaint);}// rightfor(int i = 0; i < 9; i++) {mSmallCirclePaint.setColor(i % 2 == result ? mSmallCircleYellowColor : mSmallCircleBlueColor);x = mSelfTotalWidth - mOuterCircleWidth / 2 - getPaddingRight();y =  mOuterCircleWidth*2 + (sideSize - mSmallCircleRadius * 2 * 9) / 9 * i + mSmallCircleRadius * 2 * i + getPaddingTop();canvas.drawCircle(x, y, mSmallCircleRadius, mSmallCirclePaint);}}
View Code

  

  第二步:绘制内部的圆角矩形,即卡片所在区域的背景

private void drawInnerBackground(Canvas canvas) {canvas.drawRect(mOuterCircleWidth + getPaddingLeft(), mOuterCircleWidth + getPaddingTop(),mSelfTotalWidth - mOuterCircleWidth - getPaddingRight(),mSelfTotalWidth - mOuterCircleWidth - getPaddingBottom(), mInnerPaint);}

 

  第三步: 绘制内部小卡片

private void drawInnerCards(Canvas canvas) {int left = 0, top = 0, right = 0, bottom = 0;int spaceNum = 0;for(int i = 0 ; i < 9 ; i++) {spaceNum = i % 3 + 1;left = mOuterCircleWidth + mInnerCardWidth * (i%3) + mInnerCardSpace * spaceNum + getPaddingLeft();top = mOuterCircleWidth + mInnerCardWidth * (i/3) +mInnerCardSpace * (i/3 + 1) + getPaddingTop();right = left + mInnerCardWidth;bottom = top + mInnerCardWidth;if(!mHadInitial) {mCardPositionInfoList.add(new Pair(new Pair(left, right), new Pair(top, bottom)));}drawInnerRoundCard(canvas, left, top, right, bottom, i);}mHadInitial = true;}

  

  全部绘制完成后,在onTouchEvent中处理点击事件即可,如何判定我们点击的是抽奖的区域,这里使用对比位置信息的方法,

  如下,

private int getTouchPositionInCardList(int x, int y) {if(mCardPositionInfoList != null) {int index = 1;for (Pair<Pair<Integer, Integer>,Pair<Integer, Integer>> pair : mCardPositionInfoList) {if(x > pair.first.first && x < pair.first.second && y > pair.second.first && y < pair.second.second) {return index;}index++;}}return 0;}

  将每一个小卡片的坐标信息(left,top, right, bottom)信息,保存在 ArrayList<Pair<Pair<Integer, Integer>,Pair<Integer, Integer>>>  mCardPosttionInfoList 中, 当点击VIew时获取到点击的x y 坐标和

list中保存的坐标信息做对比,如果index == 5 ,则说明点击的是抽奖所在的小卡片区域。

 

  代码托管在: https://github.com/aquarius520/LotteryView  欢迎Star 、Fork

 

转载于:https://www.cnblogs.com/sphere/p/7736807.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/390999.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

瑞立视:厚积薄发且具有“工匠精神”的中国品牌

一家成立两年的公司&#xff1a;是如何在VR行业趋于稳定的情况下首次融资就获得如此大额的金额呢&#xff1f; 2017年VR行业内宣布融资的公司寥寥无几&#xff0c;无论是投资人还是消费者对这个 “宠儿”都开始纷纷投以怀疑的目光。但就在2017年7月27日&#xff0c;深圳市一家…

苹果系统使用svg 动画_为什么要使用SVG图像:如何为SVG设置动画并使其快速闪电化

苹果系统使用svg 动画我们为什么要使用SVG&#xff1f; (Why Are We Using SVG?) The web development sector is growing at a rapid pace, and SVG (scalable vector graphics) images are becoming more popular. As vector images, SVGs are composed of mathematical equ…

CSV模块的使用

CSV模块的使用 1、csv简介 CSV (Comma Separated Values)&#xff0c;即逗号分隔值&#xff08;也称字符分隔值&#xff0c;因为分隔符可以不是逗号&#xff09;&#xff0c;是一种常用的文本 格式&#xff0c;用以存储表格数据&#xff0c;包括数字或者字符。很多程序在处理数…

Java里面遍历list的方式

问题&#xff1a;Java里面遍历list的方式 对于Java语言有点陌生&#xff0c;我尝试熟悉遍历list&#xff08;或者其他集合&#xff09;的所有方法&#xff08;或者是其他正确的语法&#xff09;和它们每一个方法的优缺点 给定 List的list对象&#xff0c;我知道有下列方法去循…

python 重启内核_Python从零开始的内核回归

python 重启内核Every beginner in Machine Learning starts by studying what regression means and how the linear regression algorithm works. In fact, the ease of understanding, explainability and the vast effective real-world use cases of linear regression is…

bzoj千题计划282:bzoj4517: [Sdoi2016]排列计数

http://www.lydsy.com/JudgeOnline/problem.php?id4517 组合数错排公式 #include<cstdio> #include<iostream>using namespace std;#define N 1000001const int mod1e97;long long fac[N],inv[N],f[N];void read(int &x) {x0; char cgetchar();while(!isdigit…

chrome启用flash_如何在Google Chrome中启用Adobe Flash Player

chrome启用flashRemember Adobe Flash player? Its that nifty software that lets websites embed videos and web games. Whole websites can even be powered by Flash.还记得Adobe Flash Player吗&#xff1f; 正是这些漂亮的软件使网站可以嵌入视频和网络游戏。 整个网站…

怎么样把Java的字符串转化为字节数组?

问题&#xff1a;怎么样把Java的字符串转化为字节数组 有没有任何方法把Java的字符串转化为字节数组 我尝试这样: System.out.println(response.split("\r\n\r\n")[1]); System.out.println("******"); System.out.println(response.split("\r\n\r\…

Forward团队-爬虫豆瓣top250项目-模块开发过程

项目托管平台地址:https://github.com/xyhcq/top250 开发模块功能: 写入文件功能 开发时间:3小时 实现将爬取到的信息写入到文件中的功能 实现过程&#xff1a; # 打开文件 fopen("top250.txt","w") 在别的队员写的代码基础上&#xff0c;加入功能代码 de…

CSS3 outline-offset 属性 项目中input会遇到

outline在一个声明中设置所有的轮廓属性。outline:颜色&#xff08;outline-line&#xff09;样式&#xff08;outline-style&#xff09;宽度&#xff08;outline-width&#xff09; outline-offset 属性对轮廓进行偏移&#xff0c;并在边框边缘进行绘制。 轮廓在两方面与边框…

回归分析中自变量共线性_具有大特征空间的回归分析中的变量选择

回归分析中自变量共线性介绍 (Introduction) Performing multiple regression analysis from a large set of independent variables can be a challenging task. Identifying the best subset of regressors for a model involves optimizing against things like bias, multi…

winform窗体模板_如何验证角模板驱动的窗体

winform窗体模板介绍 (Introduction) In this article, we will learn about validations in Angular template-driven forms. We will create a simple user registration form and implement some inbuilt validations on it. Along with the inbuilt validations, we will a…

【loj6191】「美团 CodeM 复赛」配对游戏 概率期望dp

题目描述 n次向一个栈中加入0或1中随机1个&#xff0c;如果一次加入0时栈顶元素为1&#xff0c;则将这两个元素弹栈。问最终栈中元素个数的期望是多少。 输入 一行一个正整数 n 。 输出 一行一个实数&#xff0c;表示期望剩下的人数&#xff0c;四舍五入保留三位小数。 样例输入…

查找满足断言的第一个元素

问题&#xff1a;查找满足断言的第一个元素 我刚刚开始使用Java 8的lambdas&#xff0c;我尝试去实现一些我在函数式语言里面经常用的 例如&#xff0c;大部分的函数式语言里有一些查找函数&#xff0c;针对序列或者list进行操作&#xff0c;返回使得断言为真的第一个元素。我…

Lock和synchronized的选择

学习资源:http://www.cnblogs.com/dolphin0520/p/3923167.html 一.java.util.concurrent.locks包下常用的类 1.Lock public interface Lock { void lock();//用来获取锁。如果锁已被其他线程获取&#xff0c;则进行等待。void lockInterruptibly() throws InterruptedException…

python 面试问题_值得阅读的30个Python面试问题

python 面试问题Interview questions are quite tricky to predict. In most cases, even peoples with great programming ability fail to answer some simple questions. Solving the problem with your code is not enough. Often, the interviewer will expect you to hav…

spring boot中 使用http请求

因为项目需求&#xff0c;需要两个系统之间进行通信&#xff0c;经过一番调研&#xff0c;决定使用http请求。服务端没有什么好说的&#xff0c;本来就是使用web 页面进行访问的&#xff0c;所以spring boot启动后&#xff0c;controller层的接口就自动暴露出来了&#xff0c;客…

arduino joy_如何用Joy开发Kubernetes应用

arduino joyLet’s face it: Developing distributed applications is painful.让我们面对现实&#xff1a;开发分布式应用程序很痛苦。 Microservice architectures might be great for decoupling and scalability but they are intimidatingly complex when it comes to de…

怎么样得到平台相关的换行符?

问题&#xff1a;怎么样得到平台相关的换行符&#xff1f; Java里面怎么样得到平台相关的换行符。我不可能到处都用"\n" 回答一 In addition to the line.separator property, if you are using java 1.5 or later and the String.format (or other formatting me…

scrapy常用工具备忘

scrapy常用的命令分为全局和项目两种命令&#xff0c;全局命令就是不需要依靠scrapy项目&#xff0c;可以在全局环境下运行&#xff0c;而项目命令需要在scrapy项目里才能运行。一、全局命令##使用scrapy -h可以看到常用的全局命令 [rootaliyun ~]# scrapy -h Scrapy 1.5.0 - n…