Android 自定义闪屏页广告倒计时view效果

如今APP越来越多,我们每天所使用的的软件也越来越多,可是在我们不付费的情况下,App制造商如何实现,实现收入甚至是盈利呢?答案就是在我们打开软件所必须经过的地方穿插广告,当然为了顾及用户的感受,一般都会以倒计时的形式展示给用户,用户可以选择跳过.可能是因为自己的强迫症,总想着是怎么做的,自己就尝试了一下,分享给大家的同时,顺便加深自己的理解.效果如图:


1.为了满足产品和设计,先搞几个自定义属性

1)内层背景
2)数字的颜色
3)外层圆环宽度
4)文字大小
5)外层圆环颜色
6)圆的半径

这里,我的外环颜色和文字颜色相同,具体的自定义属性如下:

<declare-styleable name="AdTimePickView">
  <attr name="mSmallCircleBg" format="color"></attr>
  <attr name="mTextSize1" format="dimension"></attr>
  <attr name="mTextColor1" format="color"></attr>
  <attr name="mProgressWidth" format="dimension"></attr>
  <attr name="mRadius" format="dimension"></attr>
 </declare-styleable>

--------------------------------------------------------------------------------

2.在自定义View的构造方法中读取自定义属性:

mProgressViewWidth = typedArray.getDimensionPixelSize(R.styleable.AdTimePickView_mProgressWidth, DEFAULT_PROGRESS_WIDTH);
  mRadius = typedArray.getDimensionPixelSize(R.styleable.AdTimePickView_mRadius1, DEFAULT_RADIUS);
  mSmallCircleBg = typedArray.getColor(R.styleable.AdTimePickView_mSmallCircleBg, Color.parseColor(DEFAULT_BG_COLOR));
  mTextSize = typedArray.getDimensionPixelSize(R.styleable.AdTimePickView_mTextSize1, DEFAULT_TEXT_SIZE);
  mTextColor = typedArray.getColor(R.styleable.AdTimePickView_mTextColor1, Color.parseColor(DEFAULT_TEXT_COLOR));

--------------------------------------------------------------------------------

3.重写onMeasure()方法,

根据宽高得出半径,为什么不适用自定义半径呢?因为根据宽高得出的半径才是这个View的内切圆半径,自定义半径只是为了在根据宽高无法得出半径的情况下才使用的.

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  mWidth = getViewSize(widthMeasureSpec, 0);
  mHeight = getViewSize(heightMeasureSpec, 1);
  mRadius = Math.min(mWidth, mHeight) / 2;
  setMeasuredDimension(mWidth, mHeight);
 }

getViewSize方法如下:

 private int getViewSize(int viewMeasureSpec, int type) {
  int viewValue = 0;
  int viewSize = MeasureSpec.getSize(viewMeasureSpec);
  int viewMode = MeasureSpec.getMode(viewMeasureSpec);
  if (MeasureSpec.EXACTLY == viewMode) {
   viewValue = viewSize;
   if (type == 0) {
    mCirCleX = viewSize / 2;
   } else {
    mCircleY = viewSize / 2;
   }
  } else {
   if (type == 0) {
    mCirCleX = mRadius;
   } else {
    mCircleY = mRadius;
   }
   viewValue = 2 * (mRadius + mProgressViewWidth);
  }
  return viewValue;
 }

--------------------------------------------------------------------------------

4.onDraw方法进行绘制

1)绘制内层圆

canvas.drawCircle(mCirCleX, mCircleY, (float) (mRadius - 1.5 * mProgressViewWidth), mPaint);

2)绘制文字,要计算好文字的位置,保持居中

 Rect textRect = getTextRect(String.valueOf(mAdTIme));
  Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics();
  int baseLine = (int) (mHeight / 2 + (fontMetrics.descent - fontMetrics.ascent) / 2 - fontMetrics.descent);
  int x = mWidth / 2 - textRect.width() / 2;
  canvas.drawText(String.valueOf(mAdTIme), x, baseLine, mTextPaint);
//获取绘制内容的Rect
private Rect getTextRect(String centerContent) {
  Rect rect = new Rect();
  mTextPaint.getTextBounds(centerContent, 0, centerContent.length(), rect);
  return rect;
 }

3)绘制外层不断刷新的圆环

原理:从360度开始每隔一段时间进行圆弧绘制,角度分别为:360,359,1,0,因此需要一个轮询器,不断的去绘制刷新.
绘制圆弧的代码:

 //保存Canvans的状态,因为绘制其他地方时,Canvas坐标系不需要变化
  canvas.save();
  //将坐标系围绕View的中心逆时针旋转90度数,为了从正上方开始绘制
  canvas.rotate(-90, mCirCleX, mCircleY);
  //计算圆弧的RectF
  RectF rectF = new RectF(mCirCleX - mRadius + mProgressViewWidth, mCirCleX - mRadius + mProgressViewWidth, mCirCleX + mRadius - mProgressViewWidth, mCirCleX + mRadius - mProgressViewWidth);
  //第四个参数表示逆时针还是顺时针绘制
  canvas.drawArc(rectF, 0, -mCurrentAngle, false, mPaint);
  //恢复坐标系
  canvas.restore();

--------------------------------------------------------------------------------

5.刷新的轮询器

1)使用RxAndroid和lambda实现

//interval操作符:从1开始每隔一段时间发射递增的数
Observable.interval(1, TIME_DIFF, TimeUnit.MILLISECONDS)
    //map操作符将发射的数据转换成我们需要的数据
    .map(value -> {
     return countAngel - value.intValue();
    })
    //限制发射的数据个数,让其停止,负责会一直发射下去
    .limit(361)
    //接收结果并处理
    .subscribe(action -> {
     if (action % 72 == 0) {
      mAdTIme = action / 72;
     }
     mCurrentAngle = action;
     AdTimePickView.this.postInvalidate();
    });

2)使用线程的方式实现

new Thread(new Runnable() {
   @Override
   public void run() {
    for (int i = 360; i>=0;i--){
     try {
      Thread.sleep(100);
     } catch (InterruptedException e) {
      e.printStackTrace();
     }
     if (i % 72 == 0) {
      mAdTIme = i / 72;
     }
     mCurrentAngle = i;
     AdTimePickView.this.postInvalidate();
    }
   }
  }).start();

OK,这样我们的广告倒计时View就完成了,欢迎大家指正.

附:整个自定义View的代码

public class AdTimePickView extends View {
private Paint mPaint;
private Paint mTextPaint;
//大圆半径
private int mRadius = 200;
//内层小圆背景
private int mSmallCircleBg = Color.parseColor("#66f1679b");
//小圆外层线条宽度
private int mProgressViewWidth = 10;
private float mCurrentAngle;
private static final int TIME_DIFF = 25;
//圆心坐标
private int mCirCleX;
private int mCircleY;
//测量之后View的宽高,绘制中心文字时需要用到
private int mWidth;
private int mHeight;
//中心文字的大小与样式
private int mTextSize;
private int mTextColor;
//广告总时间
private int mAdTIme = 5;
private Context mContext;
public AdTimePickView(Context context) {
 this(context, null);
}
public AdTimePickView(Context context, AttributeSet attrs) {
 this(context, attrs, 0);
}
public AdTimePickView(Context context, AttributeSet attrs, int defStyleAttr) {
 super(context, attrs, defStyleAttr);
 TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.AdTimePickView, defStyleAttr, 0);
 mProgressViewWidth = typedArray.getDimensionPixelSize(R.styleable.AdTimePickView_mProgressWidth, 10);
 mRadius = typedArray.getDimensionPixelSize(R.styleable.AdTimePickView_mRadius1, 100);
 mSmallCircleBg = typedArray.getColor(R.styleable.AdTimePickView_mSmallCircleBg, Color.parseColor("#66f1679b"));
 mTextSize = typedArray.getDimensionPixelSize(R.styleable.AdTimePickView_mTextSize1, 20);
 mTextColor = typedArray.getColor(R.styleable.AdTimePickView_mTextColor1, Color.parseColor("#333333"));
 //注意资源的回收
 typedArray.recycle();
 this.mContext = context;
 init();
}
private void init() {
 mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
 mPaint.setAntiAlias(true);
 mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
 mTextPaint.setColor(mTextColor);
 mTextPaint.setTextSize(mTextSize);
 mTextPaint.setStyle(Paint.Style.FILL);
 mTextPaint.setAntiAlias(true);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
 mWidth = getViewSize(widthMeasureSpec, 0);
 mHeight = getViewSize(heightMeasureSpec, 1);
 //大半径
 mRadius = Math.min(mWidth, mHeight) / 2;
 setMeasuredDimension(mWidth, mHeight);
}
private int getViewSize(int viewMeasureSpec, int type) {
 int viewValue = 0;
 int viewSize = MeasureSpec.getSize(viewMeasureSpec);
 int viewMode = MeasureSpec.getMode(viewMeasureSpec);
 if (MeasureSpec.EXACTLY == viewMode) {
  viewValue = viewSize;
  if (type == 0) {
   mCirCleX = viewSize / 2;
  } else {
   mCircleY = viewSize / 2;
  }
 } else {
  if (type == 0) {
   mCirCleX = mRadius;
  } else {
   mCircleY = mRadius;
  }
  viewValue = 2 * (mRadius + mProgressViewWidth);
 }
 return viewValue;
}
@Override
protected void onDraw(Canvas canvas) {
 super.onDraw(canvas);
 mPaint.setColor(mSmallCircleBg);
 mPaint.setStyle(Paint.Style.FILL);
 canvas.drawCircle(mCirCleX, mCircleY, (float) (mRadius - 1.5 * mProgressViewWidth), mPaint);
 //设置画笔状态
 mPaint.setColor(mTextColor);
 mPaint.setStyle(Paint.Style.STROKE);
 mPaint.setStrokeWidth(mProgressViewWidth);
 //保存Canvans的状态
 canvas.save();
 //将坐标系围绕View的中心逆时针旋转90度数
 canvas.rotate(-90, mCirCleX, mCircleY);
 RectF rectF = new RectF(mCirCleX - mRadius + mProgressViewWidth, mCirCleX - mRadius + mProgressViewWidth, mCirCleX + mRadius - mProgressViewWidth, mCirCleX + mRadius - mProgressViewWidth);
 //第四个参数表示逆时针还是顺时针绘制
 canvas.drawArc(rectF, 0, -mCurrentAngle, false, mPaint);
 canvas.restore();
 Rect textRect = getTextRect(String.valueOf(mAdTIme));
 Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics();
 int baseLine = (int) (mHeight / 2 + (fontMetrics.descent - fontMetrics.ascent) / 2 - fontMetrics.descent);
 int x = mWidth / 2 - textRect.width() / 2;
 canvas.drawText(String.valueOf(mAdTIme), x, baseLine, mTextPaint);
}
private Rect getTextRect(String centerContent) {
 Rect rect = new Rect();
 mTextPaint.getTextBounds(centerContent, 0, centerContent.length(), rect);
 return rect;
}
public void refresh() {
 final int countAngel = 360;
 Observable.interval(1, TIME_DIFF, TimeUnit.MILLISECONDS)
   .map(value -> {
    return countAngel - value.intValue();
   })
   .limit(361)
   .subscribe(action -> {
    if (action % 72 == 0) {
     mAdTIme = action / 72;
    }
    mCurrentAngle = action;
    AdTimePickView.this.postInvalidate();
   });
}
}

以上所述是小编给大家介绍的Android 自定义闪屏页广告倒计时view效果,希望对大家有所帮助,如果大家有任何疑问欢迎给我留言,小编会及时回复大家的,在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • Android中使用Handler及Countdowntimer实现包含倒计时的闪屏页面

    上一篇博文<Android中Handler使用浅析>通过实现倒计时闪屏页面的制作引出了Handler的使用方法以及实现原理,博文末尾也提到了实现过程中的Bug,有兴趣的朋友可以点击链接回去看看.今天通过使用Handler以及CountDownTimer来实现完整版的倒计时闪屏(不会出现在退出闪屏页后,依然会跳转页面的现象). 1. 实现效果如下: 1.1  正常进入跳转的效果以及log显示 1.2  倒计时未结束时退出以及log显示 对比上篇博文的实现,退出后计时停止且不会再跳到新的界面 2.

  • Android 实现闪屏页和右上角的倒计时跳转实例代码

    以前编程的时候,遇到倒计时的功能时,经常自己去写,但其实Android已经帮封装好了一个倒计时类CountDownTimer,其实是将后台线程的创建和Handler队列封装成为了一个方便的类调用. 闪屏页用到了handler和CountDownTimer类,还需配置一下Activity的主题,这里是:android:theme="@android:style/Theme.NoTitleBar.Fullscreen" 全屏主题的意思. 给大家展示下效果图: 代码如下所示: package

  • Android 自定义闪屏页广告倒计时view效果

    如今APP越来越多,我们每天所使用的的软件也越来越多,可是在我们不付费的情况下,App制造商如何实现,实现收入甚至是盈利呢?答案就是在我们打开软件所必须经过的地方穿插广告,当然为了顾及用户的感受,一般都会以倒计时的形式展示给用户,用户可以选择跳过.可能是因为自己的强迫症,总想着是怎么做的,自己就尝试了一下,分享给大家的同时,顺便加深自己的理解.效果如图: 1.为了满足产品和设计,先搞几个自定义属性 1)内层背景 2)数字的颜色 3)外层圆环宽度 4)文字大小 5)外层圆环颜色 6)圆的半径 这里

  • Android App实现闪屏页广告图的全屏显示实例

    目录 1. 适配长屏幕的全面屏 2. 适配刘海屏或者水滴屏 凹形屏幕的显示模式 1. 适配长屏幕的全面屏 至于全屏展示,就得做适配工作,有以下两种方式可进行适配: 在 Android 8.0(API 26)及更高版本中,我们可以在 标签中使用 android:MaxAspectRatio 来声明其支持的屏幕最大宽高比. 比如我们可以声明最大宽高比为 2.4: <!-- Render on full screen up to screen aspect ratio of 2.4 --> <

  • Android实现闪屏页效果

    本文实例为大家分享了Android实现闪屏页效果的具体代码,供大家参考,具体内容如下 1.效果图 2.闪屏页逻辑及布局 2.1 activity_splash.xml <?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http:

  • Android应用闪屏页延迟跳转的三种写法

    闪屏页也称之为欢迎页.在打开一个App时,我们第一眼看到的往往是一个闪屏页面,之所以叫闪屏页,是因为它出现之后会短暂地停留几秒钟再跳转到其他页面.闪屏页除了使用户体验更好外,还能给app留出初始化数据的时间.下面,是我总结的闪屏页的三种写法. 准备工作 打开Android Studio,新建一个projectMyStudy,新建一个SplashActivity并在清单文件中将其设置为启动Activity.在布局文件中将SplashActivity的背景设置为一张图片(可以是手机壁纸). <?xm

  • Android Handler实现闪屏页倒计时代码

    我就废话不多说了,大家还是直接看代码吧~ package com.zjx.todayinfomation; import android.os.Handler; public class CustomCountDownTimer implements Runnable{ // 1.实时去回调 这个时候是什么时间 倒计时到几点 观察者设计模式 // 2.支持传入总时间 动态传入 // 3.每过一秒 总秒数 -1 // 4.总时间倒计时为0时候 回调完成状态 private int time; //

  • Android中闪屏实现方法小结(普通闪屏、倒计时闪屏、倒计时+动画闪屏)

    一.项目目录结构 二.activity_main.xml代码 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match

  • Android实现闪屏及注册和登录界面之间的切换效果

    在没给大家介绍正文之前先给大家说下实现思路: 先分别实现闪屏.注册界面.登录界面的活动,再用Intent将相关的活动连接起来,实现不同活动之间的跳转.此次试验代码较多,我只列出主要代码,详细的代码可用底部的下载链接下载. 一.实验效果图: 二.主要代码: (1)WelcomeActivity.Java(这部分代码实现的是第一页的欢迎页面) package com.example.flashscreendemo; import android.app.Activity; import androi

  • 详谈闪屏页相关处理

    根据功能模块划分(Android开发推荐此方法) - Activity   mobilesafe.activty     - 后台服务   mobilesafe.service     - 广播接受者 mobilesafe.receiver     - 数据库 mobilesafe.db.dao     - 对象(java bean) mobilesafe.domain/bean     - 自定义控件 mobilesafe.view     - 工具类 mobilesafe.utils    

  • Flutter启动页(闪屏页)的具体实现及原理详析

    为什么要有启动页? 在以下文章中,启动页就是闪屏页. 现在大部分App都有启动页,那么为什么要有启动页?这是个值得思考的问题,如果没有启动页会怎样,大部分的App会白屏(也有可能是黑屏,主题设置有关系)非常短的时间,然后才能展示App的内容. 那么问题来了,一定要有启动页吗?答案:不是,而且是尽可能不要有启动页,因为启动页会让用户体验不够连贯,甚至IOS在开发手册上就不推荐使用启动页. 我们深入思考一下,既然不推荐为什么这样流行,答案非常简单,启动页的成本非常低,如果你想把的App启动优化到一个

随机推荐