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;深圳市一家…

CSV模块的使用

CSV模块的使用 1、csv简介 CSV (Comma Separated Values)&#xff0c;即逗号分隔值&#xff08;也称字符分隔值&#xff0c;因为分隔符可以不是逗号&#xff09;&#xff0c;是一种常用的文本 格式&#xff0c;用以存储表格数据&#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…

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

回归分析中自变量共线性介绍 (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…

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…

机器学习模型 非线性模型_机器学习:通过预测菲亚特500的价格来观察线性模型的工作原理...

机器学习模型 非线性模型Introduction介绍 In this article, I’d like to speak about linear models by introducing you to a real project that I made. The project that you can find in my Github consists of predicting the prices of fiat 500.在本文中&#xff0c;…

10款中小企业必备的开源免费安全工具

10款中小企业必备的开源免费安全工具 secist2017-05-188共527453人围观 &#xff0c;发现 7 个不明物体企业安全工具很多企业特别是一些中小型企业在日常生产中&#xff0c;时常会因为时间、预算、人员配比等问题&#xff0c;而大大减少或降低在安全方面的投入。这时候&#xf…

图片主成分分析后的可视化_主成分分析-可视化

图片主成分分析后的可视化If you have ever taken an online course on Machine Learning, you must have come across Principal Component Analysis for dimensionality reduction, or in simple terms, for compression of data. Guess what, I had taken such courses too …

TP引用样式表和js文件及验证码

TP引用样式表和js文件及验证码 引入样式表和js文件 <script src"__PUBLIC__/bootstrap/js/jquery-1.11.2.min.js"></script> <script src"__PUBLIC__/bootstrap/js/bootstrap.min.js"></script> <link href"__PUBLIC__/bo…

pytorch深度学习_深度学习和PyTorch的推荐系统实施

pytorch深度学习The recommendation is a simple algorithm that works on the principle of data filtering. The algorithm finds a pattern between two users and recommends or provides additional relevant information to a user in choosing a product or services.该…

Java 集合-集合介绍

2017-10-30 00:01:09 一、Java集合的类关系图 二、集合类的概述 集合类出现的原因&#xff1a;面向对象语言对事物的体现都是以对象的形式&#xff0c;所以为了方便对多个对象的操作&#xff0c;Java就提供了集合类。数组和集合类同是容器&#xff0c;有什么不同&#xff1a;数…

Exchange 2016部署实施案例篇-04.Ex基础配置篇(下)

上二篇我们对全新部署完成的Exchange Server做了基础的一些配置&#xff0c;今天继续基础配置这个话题。 DAG配置 先决条件 首先在配置DGA之前我们需要确保DAG成员服务器上磁盘的盘符都是一样的&#xff0c;大小建议最好也相同。 其次我们需要确保有一块网卡用于数据复制使用&…

数据库课程设计结论_结论:

数据库课程设计结论In this article, we will learn about different types[Z Test and t Test] of commonly used Hypothesis Testing.在本文中&#xff0c;我们将学习常用假设检验的不同类型[ Z检验和t检验 ]。 假设是什么&#xff1f; (What is Hypothesis?) This is a St…

配置Java_Home,临时环境变量信息

一、内容回顾 上一篇博客《Java运行环境的搭建---Windows系统》 我们说到了配置path环境变量的目的在于控制台可以在任意路径下都可以找到java的开发工具。 二、配置其他环境变量 1. 原因 为了获取更大的用户群体&#xff0c;所以使用java语言开发系统需要兼容不同版本的jdk&a…

网页缩放与窗口缩放_功能缩放—不同的Scikit-Learn缩放器的效果:深入研究

网页缩放与窗口缩放内部AI (Inside AI) In supervised machine learning, we calculate the value of the output variable by supplying input variable values to an algorithm. Machine learning algorithm relates the input and output variable with a mathematical func…

Python自动化开发01

一、 变量变量命名规则变量名只能是字母、数字或下划线的任意组合变量名的第一个字符不能是数字以下关键字不能声明为变量名 [and, as, assert, break, class, continue, def, del, elif, else, except, exec, finally, for, from, global, if, import, in, is, lambda, not,…

未越狱设备提取数据_从三星设备中提取健康数据

未越狱设备提取数据Health data is collected every time you have your phone in your pocket. Apple or Android, the phones are equipped with a pedometer that counts your steps. Hence, health data is recorded. This data could be your one free data mart for a si…

[BZOJ2599][IOI2011]Race 点分治

2599: [IOI2011]Race Time Limit: 70 Sec Memory Limit: 128 MBSubmit: 3934 Solved: 1163[Submit][Status][Discuss]Description 给一棵树,每条边有权.求一条简单路径,权值和等于K,且边的数量最小.N < 200000, K < 1000000 Input 第一行 两个整数 n, k第二..n行 每行三…

分词消除歧义_角色标题消除歧义

分词消除歧义折磨数据&#xff0c;它将承认任何事情 (Torture the data, and it will confess to anything) Disambiguation as defined in the vocabulary.com dictionary refers to the removal of ambiguity by making something clear and narrowing down its meaning. Whi…

北航教授李波:说AI会有低潮就是胡扯,这是人类长期的追求

这一轮所谓人工智能的高潮&#xff0c;和以往的几次都有所不同&#xff0c;那是因为其受到了产业界的极大关注和参与。而以前并不是这样。 当今世界是一个高度信息化的世界&#xff0c;甚至我们有一只脚已经踏入了智能化时代。而在我们日常交流和信息互动中&#xff0c;迅速发…