热门标签 | HotTags
当前位置:  开发笔记 > 运维 > 正文

Android打造流畅九宫格抽奖活动效果

抽奖活动有很多种形式,转盘抽奖,九宫格抽奖,刮刮卡抽奖,这篇文章主要为大家详细介绍了如何打造流畅九宫格抽奖活动效果,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

因为company项目中需要做九宫格抽奖活动,以前都没有做过类似的功能,虽然之前在浏览大神们的博客中,无意中也看到了好多关于抽奖的项目,但因为项目中没有需要,一直都没有点击进去看。这次不去看估计不行。直到公司计划要做抽奖功能,才迫不得已上网查找demo

网上找了大半天,好不容易找到了几个demo,下载下来,解压缩包发现竟然里面空空如也,只有几张九宫格的图片,害我白白浪费了几个CSDN积分。后面在eoe网站那发现了一个demo,于是好开心,下载下来后马上导入到工程中,运行看了效果,九宫格是出来了,但效果真不敢恭维,主要是运行不流畅。但我还是进去稍微看了一下demo,基本思路是这样的:定义好九宫格界面,然后开启子线程不断循环修改状态,再通过handler发送消息到主线程中修改界面(子线程不能直接修改界面)。

这个demo虽然功能上实现了,但不是我想要的效果,因为我这一关都不能通过,到了产品那边更加不用说了。那怎么办呢?

于是我想到了一个控件,叫做SurfaceView,做游戏开发的同志们,应该对这个控件不陌生吧?首先介绍一下这个控件:
1.SurfaceView继承于View,多用于游戏开发中
2.可以直接在子线程中运行(其他UI控件都必须在主线程中运行的)。
3.一般的UI控件自定义时都是重写onDraw方法,但在SurfaceView中是通过SurfaceHolder获取Canvas来绘制图形的

好了,来吧各位,先来看看效果图:

这样,下面我开始根据我的想法,把自定义九宫格的步骤说一下。

步骤:

1.计算各位方块的位置
2.绘制每个奖品的方块(主要让界面更加好看)
3.绘制奖品图
4.计算旋转方块的下一步位置
5.绘制旋转方块
6.监听点击开始按钮事件

主要核心技术:
SurfaceView,SurfaceHolder

OK,有了基本步骤,接下来就是根据步骤一步一步来进行了。
在开始绘制九宫格之前,我们先重写onMeasure方法,主要是为了让九宫格成为一个正方形,这样看起来体验更好,基本代码如下:

public class LotteryView extends SurfaceView{

  /**
   * holder
   */
  private SurfaceHolder mHolder;


  private Listprizes;
  private boolean flags;  //抽奖开关

  private int lottery=6;  //设置中奖号码

  private int current=2;  //抽奖开始的位置

  private int count=0;  //旋转次数累计

  private int countDown;  //倒计次数,快速旋转完成后,需要倒计多少次循环才停止

  //旋转抽奖的方块默认颜色
  private int transfer= 0xffff0000;

  private int MAX=50;  //最大旋转次数
  /** 
   * 重新测量
   */ 
  @Override 
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) 
  { 
    super.onMeasure(widthMeasureSpec, heightMeasureSpec); 
    int width = Math.min(getMeasuredWidth(), getMeasuredHeight()); 
    setMeasuredDimension(width, width); 
  }
}


SurfaceView一般不是通过重写onDraw方法来绘制控件的,那么怎么获取到Canvas呢?主要是通过SurfaceHolder监听Callback事件来获取的
基本代码如下:

/**
   * holder
   */
  private SurfaceHolder mHolder;
  public LotteryView(Context context, AttributeSet attrs) {
    super(context, attrs);
    mHolder = this.getHolder();
    //监听CallBack
    mHolder.addCallback(this);
  }

  public LotteryView(Context context) {
    this(context,null);
  }


现在有了对象SurfaceHolder对象,我们就可以获取到Canvas对象了,下面开始真正的绘制工作。

1.计算方块的具体显示位置
2.绘制每个奖品的方块

  //绘制背景
  private void drawBg(Canvas canvas) {
    //清除已绘制的图形
    canvas.drawColor(Color.WHITE, Mode.CLEAR);
    //获取控件的宽度,因为要绘制九宫格,所以要平局分成三列
    int width = getMeasuredWidth()/3;
    int x1=0;
    int y1=0;

    int x2=0;
    int y2=0;

    int len = (int) Math.sqrt(prizes.size());

    for(int x=0;x

解析:prizes 是一个集合,里面封装了奖品的一些基本信息,x1,y1,x2,y2分别是绘制奖品容器正方形的左上顶点和右下顶点,

通过观察发现,每一个方块位置都有一定的关系,即 x1=getPaddingLeft()+width*(Math.abs(index)%len);

y1=getPaddingTop()+width*(index/len); 
x2=x1+width; 
y2=y1+width; 

有了这些点的关系,就可以通过canvas.drawRect(rect, paint);绘制出方块了

3.绘制奖品图

  //绘制奖品
  private void drawPrize(Canvas canvas) {
    int width = getMeasuredWidth()/3;
    int x1=0;
    int y1=0;

    int x2=0;
    int y2=0;

    int len = (int) Math.sqrt(prizes.size());

    for(int x=0;x

通过了步骤1,2知道了方块的位置关系,就可以轻松的根据这些关系绘制出奖品来,Rect rect=new Rect(x1+width/6,y1+width/6,x2-width/6,y2-width/6);是让奖品比方块缩小一些,这样看起来会更自然一点。

4.计算旋转方块的下一步位置

  //下一步
  public int next(int position,int len){
    int current=position;
    if(current+1

position是当前旋转方块的位置,len是3

5.绘制旋转方块

  //绘制旋转的方块
  private void drawTransfer(Canvas canvas) {
    int width = getMeasuredWidth()/3;
    int x1;
    int y1;

    int x2;
    int y2;
    int len = (int) Math.sqrt(prizes.size());
    //得到下一步方块的位置
    current=next(current, len);
    x1=getPaddingLeft()+width*(Math.abs(current)%len);
    y1=getPaddingTop()+width*((current)/len);

    x2=x1+width;
    y2=y1+width;

    Rect rect=new Rect(x1,y1,x2,y2);
    Paint paint=new Paint();
    paint.setColor(transfer);
    canvas.drawRect(rect, paint);
  }

6.监听点击开始按钮事件

  private OnTransferWinningListener listener;

  public void setOnTransferWinningListener(OnTransferWinningListener listener){
    this.listener=listener;
  }

  public interface OnTransferWinningListener{
    /**
     * 中奖回调
     * @param position
     */
    void onWinning(int position);
  }
    @Override
  public boolean onTouchEvent(MotionEvent event) {
    handleTouch(event);
    return super.onTouchEvent(event);
  }
  /**
   * 触摸
   * @param event
   */
  public void handleTouch(MotionEvent event) {


    Point touchPoint=new Point((int)event.getX()-getLeft(),(int)event.getY());
    switch(event.getAction()){
    case MotionEvent.ACTION_DOWN:
      Prize prize = prizes.get(Math.round(prizes.size())/2);
      if(prize.isClick(touchPoint)){
        if(!flags){
          setStartFlags(true);
          prize.click();
        }
      }
      break ;
    default:
      break ;
    }
  }

//控制旋转
  private void controllerTransfer() {
    if(count>MAX){
      countDown++;
      SystemClock.sleep(count*5);
    }else{
      SystemClock.sleep(count*2);
    }

    count++;
    if(countDown>2){
      if(lottery==current){
        countDown=0;
        count=0;
        setStartFlags(false);

        if(listener!=null){
          //切换到主线程中运行
          post(new Runnable() {

            @Override
            public void run() {
              listener.onWinning(current);
            }
          });

        }
      }
    }
  }

至此,基本的自定义工作已经差不多了,使用demo如下:

<&#63;xml version="1.0" encoding="utf-8"&#63;>


  




public class HomeActivity extends Activity {

  LotteryView nl;
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.act_home);

    nl=(LotteryView) findViewById(R.id.nl);

    int[]prizesIcon={R.drawable.danfan,R.drawable.meizi,R.drawable.iphone,R.drawable.f015,R.drawable.arrow,R.drawable.f040,R.drawable.ipad,R.drawable.spree_icon,R.drawable.spree_success_icon};
    final Listprizes=new ArrayList();
    for(int x=0;x<9;x++){
      Prize lottery=new Prize();
      lottery.setId(x+1);
      lottery.setName("Lottery"+(x+1));
      Bitmap bitmap = BitmapFactory.decodeResource(getResources(), prizesIcon[x]);
      lottery.setIcon(bitmap);
      if((x+1)%2==0){
        lottery.setBgColor(0xff4fccee);
      }else if(x==4){
        lottery.setBgColor(0xffffffff);
      }else{
        lottery.setBgColor(0xff00ff34);
      }

      prizes.add(lottery);
    }
    nl.setPrizes(prizes);
    nl.setOnTransferWinningListener(new OnTransferWinningListener() {

      @Override
      public void onWinning(int position) {
        Toast.makeText(getApplicationContext(), prizes.get(position).getName(), Toast.LENGTH_SHORT).show();
      }
    });
  }
}

运行效果非常流畅

LotteryView整体demo:

package com.example.test;

import java.util.List;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.PorterDuff;
import android.graphics.PorterDuff.Mode;
import android.graphics.Rect;
import android.os.SystemClock;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
import android.view.SurfaceView;


public class LotteryView extends SurfaceView implements Callback{

 /**
 * holder
 */
 private SurfaceHolder mHolder;


 private Listprizes;
 private boolean flags;

 private int lottery=6; //设置中奖号码

 private int current=2; //抽奖开始的位置

 private int count=0; //旋转次数累计

 private int countDown; //倒计次数,快速旋转完成后,需要倒计多少次循环才停止


 private int transfer= 0xffff0000;

 private int MAX=50; //最大旋转次数

 private OnTransferWinningListener listener;

 public void setOnTransferWinningListener(OnTransferWinningListener listener){
 this.listener=listener;
 }

 public interface OnTransferWinningListener{
 /**
  * 中奖回调
  * @param position
  */
 void onWinning(int position);
 }


 /**
 * 设置中奖号码
 * @param lottery
 */
 public void setLottery(int lottery) {
 if(prizes!=null&&Math.round(prizes.size()/2)==0){
  throw new RuntimeException("开始抽奖按钮不能设置为中奖位置!");
 }
 this.lottery = lottery;
 }

 /**
 * 设置转盘颜色
 * @param transfer
 */
 public void setTransfer(int transfer) {
 this.transfer = transfer;
 }

 /**
 * 设置奖品集合
 * @param prizes
 */
 public void setPrizes(Listprizes){
 this.prizes=prizes;
 }


 @Override
 public boolean onTouchEvent(MotionEvent event) {
 handleTouch(event);
 return super.onTouchEvent(event);
 }

 /**
 * 触摸
 * @param event
 */
 public void handleTouch(MotionEvent event) {


 Point touchPoint=new Point((int)event.getX()-getLeft(),(int)event.getY());
 switch(event.getAction()){
 case MotionEvent.ACTION_DOWN:
  Prize prize = prizes.get(Math.round(prizes.size())/2);
  if(prize.isClick(touchPoint)){
  if(!flags){
   setStartFlags(true);
   prize.click();
  }
  }
  break ;
 default:
  break ;
 }
 }
 private class SurfaceRunnable implements Runnable{
 @Override
 public void run() {
  while(flags){
  Canvas canvas=null;
  try {
   canvas = mHolder.lockCanvas();

   drawBg(canvas);

   drawTransfer(canvas);

   drawPrize(canvas);

   controllerTransfer();
  } catch (Exception e) {
   e.printStackTrace();
  }finally{
   //涓轰簡璁╂瘡娆$粯鍒跺浘褰㈡椂鑳藉椤哄埄杩涜锛屾渶濂藉皢瑙i攣鏀惧埌寮傚父涓繘琛屽鐞嗭紝涔熷氨鏄锛屽鏋渃anvas涓嶄负绌猴紝閮藉皢鍏跺叧闂紝璁╀笅涓&#65533;娆″惊鐜兘澶熼『鍒╄繘琛岀粯鍒&#65533;
   if(canvas!=null)
   mHolder.unlockCanvasAndPost(canvas); 
  }
  }
 }
 }

 //绘制背景
 private void drawBg(Canvas canvas) {
 canvas.drawColor(Color.WHITE, Mode.CLEAR);
 int width = getMeasuredWidth()/3;
 int x1=0;
 int y1=0;

 int x2=0;
 int y2=0;

 int len = (int) Math.sqrt(prizes.size());

 for(int x=0;xMAX){
  countDown++;
  SystemClock.sleep(count*5);
 }else{
  SystemClock.sleep(count*2);
 }

 count++;
 if(countDown>2){
  if(lottery==current){
  countDown=0;
  count=0;
  setStartFlags(false);

  if(listener!=null){
   //切换到主线程中运行
   post(new Runnable() {

   @Override
   public void run() {
    listener.onWinning(current);
   }
   });

  }
  }
 }
 }

 public void setStartFlags(boolean flags){
 this.flags=flags;
 }

 //绘制奖品
 private void drawPrize(Canvas canvas) {
 int width = getMeasuredWidth()/3;
 int x1=0;
 int y1=0;

 int x2=0;
 int y2=0;

 int len = (int) Math.sqrt(prizes.size());

 for(int x=0;x

 以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。


推荐阅读
  • 本文详细介绍了ejabberd中的验证码服务、接收器以及服务器间通信的监督者和工作进程,包括它们的启动方式和主要功能。 ... [详细]
  • 本文探讨了如何在Android框架下通过自定义资源文件实现系统风格的统一,包括系统资源文件的位置、引用方法、系统主题的设置及修改等内容。 ... [详细]
  • 深入理解Django中的AJAX应用
    本文详细介绍了AJAX技术及其在Django框架中的应用。AJAX,即异步JavaScript和XML,允许网页在不重新加载整个页面的情况下与服务器交换数据并更新部分网页。 ... [详细]
  • Solr的安装与部署指南
    Solr作为一款独立的企业级搜索应用服务器,支持Web-service风格的API接口,允许用户通过HTTP请求提交XML文件以创建索引或执行搜索操作。本文将详细介绍Solr的安装步骤及配置方法。 ... [详细]
  • 本文深入探讨了Redis中的两种主要持久化方式——RDB(Redis Database)和AOF(Append Only File),并详细解析了两者的实现机制、优缺点以及在实际应用中的选择策略。 ... [详细]
  • 本文介绍了两种有效的方法来检查Android应用是否拥有特定权限,如媒体读写权限(media_rw)。通过这些方法,开发者和安全人员可以更好地了解应用的行为,确保其不会滥用权限。 ... [详细]
  • 深入理解Kafka架构
    本文将详细介绍Kafka的内部工作机制,包括其工作流程、文件存储机制、生产者与消费者的具体实现,以及如何通过高效读写技术和Zookeeper支持来确保系统的高性能和稳定性。 ... [详细]
  • 使用Solr从MySQL导入数据构建全量索引
    为了更好地掌握Solr的各项功能,本文档将在本地Windows环境中演示如何从MySQL数据库中导入数据至Solr,并构建全量索引。这将有助于开发者熟悉Solr的数据处理流程,尤其是在无法直接在生产服务器上进行实践的情况下。 ... [详细]
  • Java性能优化指南 | 制定有效的性能优化策略
    探讨Java应用性能优化的方法与策略,包括性能测试技巧、常见问题及解决方案,旨在帮助开发者提升系统性能。 ... [详细]
  • 在尝试将SpringBoot与MyBatis框架进行集成时,遇到了一个常见的问题:org.apache.ibatis.builder.BuilderException。此错误通常指示XML配置文件中存在语法或结构上的问题。本文将探讨具体原因及解决方案。 ... [详细]
  • Apache Tomcat 安装与目录解析
    本文详细介绍了如何在Windows系统上安装Apache Tomcat 8版本,并对其主要目录结构进行了说明。包括下载、解压、配置环境变量等步骤,以及如何通过检查端口占用情况解决启动问题。 ... [详细]
  • 前言叨逼叨iOS上传文件,可能有很多第三方的框架之类的,比如AFN或者Alamofire之类的框架,但是今天要谈论的是原生的API是如何进行文件上传。兵 ... [详细]
  • Gradle基础概念与实践指南
    本文详细介绍了Gradle的基本概念、Groovy语言基础、Gradle的生命周期、项目管理以及任务配置等内容,旨在帮助开发者更好地理解和使用Gradle构建工具。 ... [详细]
  • 本文介绍了在Makefile及Android.mk文件中添加打印输出信息的方法,并详细解析了Android编译过程中的关键步骤,包括环境变量的设置与编译脚本的执行。 ... [详细]
  • Web 安全实践:确保 Tomcat 仅支持域名访问
    为何服务器需限制直接通过IP地址访问?首先,未备案的公网IP可能面临监管机构的封禁,影响域名的正常访问。其次,使用IP地址访问可能会导致安全工具检测出内部IP泄露的风险,增加潜在的安全隐患。 ... [详细]
author-avatar
活跃的爱味儿县_454
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有