简介:所谓遮罩动画,就是利用遮罩的动画原理来实现的。可以看到的动画是遮罩层区域的内容。

说说这个实现原理:原理其实很简单,只要了解android画笔怎么工作的就非常容易理解。一:指定绘制图片或者层的大小(矩形-x,y,width,height)二:指定刷新的区域(矩形-x,y,width,height)

例如一张100*100的图片,我们可以只让它绘制矩形为:0,0,30,30,在Android里面已经提供了这个函数:drawBitmap(bitmap, src, dst, paint)Draw the specified bitmap, scaling/translating automatically to fill the destination rectangle. If the source rectangle is not null, it specifies the subset of the bitmap to draw.Note: if the paint contains a maskfilter that generates a mask which extends beyond the bitmap's original width/height (e.g. BlurMaskFilter), then the bitmap will be drawn as if it were in a Shader with CLAMP mode. Thus the color outside of the original width/height will be the edge color replicated.

This function ignores the density associated with the bitmap. This is because the source and destination rectangle coordinate spaces are in their respective densities, so must already have the appropriate scaling factor applied.

Parameters:bitmap The bitmap to be drawnsrc May be null. The subset of the bitmap to be drawndst The rectangle that the bitmap will be scaled/translated to fit intopaint May be null. The paint used to draw the bitmap那么上面是绘制图片指定大小。再怎么设置屏幕刷新区域呢?Andriod画布类也提供了方法,invalidate(l, t, r, b)Mark the area defined by the rect (l,t,r,b) as needing to be drawn. The coordinates of the dirty rect are relative to the view. If the view is visible, onDraw(android.graphics.Canvas) will be called at some point in the future. This must be called from a UI thread. To call from a non-UI thread, call postInvalidate().Parameters:l the left position of the dirty regiont the top position of the dirty regionr the right position of the dirty regionb the bottom position of the dirty region

通过上面两个可以实现很多动画。不过这并不是我们要讲的重点,这只是告诉大家实现的原理。我们不需要这么复杂,因为Andrid已经封装好了函数,我们拿来用就行了!clipRect(l, t, r, b)Mark the area defined by the rect (l,t,r,b) as needing to be drawn. The coordinates of the dirty rect are relative to the view. If the view is visible, onDraw(android.graphics.Canvas) will be called at some point in the future. This must be called from a UI thread. To call from a non-UI thread, call postInvalidate().Parameters:l the left position of the dirty regiont the top position of the dirty regionr the right position of the dirty regionb the bottom position of the dirty region

看下面的动态图片

源码部分:

package com.lee.seekbar;

import android.annotation.SuppressLint;

import android.content.Context;

import android.content.res.TypedArray;

import android.graphics.Bitmap;

import android.graphics.BitmapFactory;

import android.graphics.Canvas;

import android.graphics.Paint;

import android.graphics.Path;

import android.graphics.RectF;

import android.util.AttributeSet;

import android.view.MotionEvent;

import android.widget.ImageView;

@SuppressLint("ClickableViewAccessibility")

public class ArcSeekBar extends ImageView {

Bitmap fullBitmap;

Paint fullPaint;

Bitmap buttonBitmap;

Paint buttonPaint;

// 859 419原始大小

int ARC_START_X = 87;// 弧度起始x坐标

int ARC_START_Y = 142;// 弧度起始y坐标

// 可运行轨迹 半径

float RUN_RADIUS = 375;

float BITMAP_SCAL = 0;// 图片显示和真实比例

// 图片中心坐标

int PIC_X;

// 按钮中心

int BTN_CX;// 按钮宽的一半

int BTN_CY;// 按钮高的一半

// 裁剪区

RectF rectF;// 遮罩坐标,大小

Path path;// 遮罩路径

float startAngle;// 运行起始角度

final float sweepAngle = 180;// 半圆形遮罩层

final int level = 16;// 级别

int[] levelAreaX = new int[level];// x坐标活动区间

float[] levelArea = new float[level];// 按钮活动弧度区间

float offset_btn;// 按钮微调

float minArc = 18;// 起始弧度 最小值 用PS测量可得,该值不受屏幕大小影响

float maxArc = 157;// 结束弧度 最大值,该值不受屏幕大小影响

SeekBarActionListener seekBarActionListener;

boolean uniform;// 是否匀速

public ArcSeekBar(Context context, AttributeSet attrs) {

super(context, attrs);

// TODO Auto-generated constructor stub

TypedArray a = context.obtainStyledAttributes(attrs,

R.styleable.ArcSeekBar);

int resID = a.getResourceId(R.styleable.ArcSeekBar_fullbitmap, 0);

fullBitmap = BitmapFactory.decodeResource(getResources(), resID);

resID = a.getResourceId(R.styleable.ArcSeekBar_buttonbitmap, 0);

buttonBitmap = BitmapFactory.decodeResource(getResources(), resID);

int dstWidth = (int) (buttonBitmap.getWidth() * 1.5f);

int dstHeight = (int) (buttonBitmap.getHeight() * 1.5f);

buttonBitmap = Bitmap.createScaledBitmap(buttonBitmap, dstWidth,

dstHeight, false);

BTN_CX = buttonBitmap.getWidth() >> 1;

BTN_CY = buttonBitmap.getHeight() >> 1;

fullPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

buttonPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

offset_btn = getResources().getDimensionPixelSize(R.dimen.size_last);

a.recycle();

// 计算出当前屏幕显示图片放大或缩小的比例

PIC_X = fullBitmap.getWidth() >> 1;

BITMAP_SCAL = fullBitmap.getWidth() / 859f;

// 轨迹

ARC_START_X = (int) (ARC_START_X * BITMAP_SCAL);

ARC_START_Y = (int) (ARC_START_Y * BITMAP_SCAL);

RUN_RADIUS -= (BTN_CX >> 1);// 运行半径减去按钮半径

RUN_RADIUS = RUN_RADIUS * BITMAP_SCAL;

float offset = (maxArc - minArc) / (level - 1);// 每个区间的间隔的弧度

int offsetX = ((int) RUN_RADIUS << 1) / (level - 1);

for (int i = 0, j = levelArea.length - 1; i < levelArea.length; i++, j--) {

levelArea[j] = minArc + offset * i;

levelAreaX[i] = ARC_START_X + offsetX * i;

}

rectF = new RectF(0, -fullBitmap.getHeight(), fullBitmap.getWidth(),

fullBitmap.getHeight());

path = new Path();

// 运动范围157~18

// PS测试数据计算得出

startAngle = getRotation(PIC_X, 0, ARC_START_X, ARC_START_Y) + 90;

uniform = true;

}

@Override

protected void onDraw(Canvas canvas) {

// TODO Auto-generated method stub

super.onDraw(canvas);

drawArc(canvas);

}

private void drawArc(Canvas canvas) {

if (fullBitmap != null) {

canvas.save();

path.reset();

path.addArc(rectF, startAngle, sweepAngle);

path.close();

canvas.clipPath(path);

canvas.drawBitmap(fullBitmap, 0, 0, fullPaint);

canvas.restore();

}

if (buttonBitmap != null) {

canvas.save();

float cx = (float) Math.cos(Math.toRadians(startAngle))

* RUN_RADIUS + PIC_X;

float cy = (float) Math.sin(Math.toRadians(startAngle))

* RUN_RADIUS;

canvas.rotate(startAngle - 90, cx, cy);

float last_offset = nLastIndex == levelArea.length - 1 ? 2 : 0;// 调最后一个

canvas.drawBitmap(buttonBitmap, cx - BTN_CX, cy - BTN_CY

+ offset_btn + last_offset, buttonPaint);

canvas.restore();

}

}

float downX;

int nIndex;

int nLastIndex;

@Override

public boolean onTouchEvent(MotionEvent event) {

// TODO Auto-generated method stub

if (uniform) {

float x1 = event.getX();

float y1 = event.getY();

startAngle = getRotation(PIC_X, 0, x1, y1) + 90;

}

if (startAngle > maxArc)

return true;

if (startAngle < minArc)

return true;

switch (event.getAction()) {

case MotionEvent.ACTION_DOWN:

downX = event.getX();

break;

case MotionEvent.ACTION_MOVE:

float X = event.getX();

if (X - downX > 0) {

// 往右

nIndex = getOffsetIndex(X);

downX = X;

} else {

// 往左

nIndex = getOffsetIndex(X);

downX = X;

}

if (nIndex >= 0 && nIndex < levelArea.length) {

if (!uniform)// 不是匀速

startAngle = levelArea[nIndex];

if (seekBarActionListener != null && nLastIndex != nIndex)

seekBarActionListener.seek(nIndex);

nLastIndex = nIndex;

}

invalidate();

break;

case MotionEvent.ACTION_UP:

invalidate();

break;

}

return super.onTouchEvent(event);

}

public void setnIndex(int nIndex) {

this.nIndex = nIndex;

startAngle = levelArea[nIndex];

postInvalidate();

}

// 获取当前益所在的区间 0~level-1

private int getOffsetIndex(float x) {

for (int i = 0; i < levelAreaX.length; i++) {

if (i < levelAreaX.length - 1) {

if (x >= levelAreaX[i] && x <= levelAreaX[i + 1])

return i;

} else {

if (x >= levelAreaX[i])

return i;

}

}

return -1;

}

private float getRotation(float x1, float y1, float x2, float y2) {

float value = (float) Math.toDegrees(Math.atan2(y2 - y1, x2 - x1));

return value - 90f;// 坐标系

}

public void setSeekBarActionListener(

SeekBarActionListener seekBarActionListener) {

this.seekBarActionListener = seekBarActionListener;

}

public boolean isUniform() {

return uniform;

}

public void setUniform(boolean uniform) {

this.uniform = uniform;

}

public interface SeekBarActionListener {

public void seek(int index);

}

}

android 黑色遮罩按钮,打造一个Android的遮罩层SeekBar相关推荐

  1. 单独组件_阿里P8年薪百万大牛-教你打造一个Android组件化开发框架

    作者简介 本篇来自 lucky_billy 的投稿,分享了他的开源组件化框架,详细地讲解框架形成的思路,希望对大家有所帮助. lucky_billy 的博客地址: http://blog.csdn.n ...

  2. 《教我兄弟学Android逆向01 编写第一个Android程序》

    前言 之所以准备写这一系列逆向的教程是因为有一些同学私信我说自己想学习Android逆向但是不知道怎么去学习 包括自己身边的一些计算机专业的同学 在大学里面老师讲的那些东西要么是自己不感兴趣 要么是自 ...

  3. 《教我兄弟学Android逆向03 破解第一个Android游戏 》

    上一篇 <教我兄弟学Android逆向02  破解第一个Android程序  >我带着你破解了我们自己编的一个小程序 里面我分析并讲解的一些smali语法你都记住了 给你布置的课后作业你发 ...

  4. android组件化开发视频教程,教你打造一个Android组件化开发框架

    作者简介 本篇来自 lucky_billy 的投稿,分享了他的开源组件化框架,详细地讲解框架形成的思路,希望对大家有所帮助. lucky_billy 的博客地址: 解读开源框架设计思想 B站学习视频 ...

  5. 教你打造一个Android组件化开发框架

    *本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布 CC:Component Caller,一个android组件化开发框架, 已开源,github地址:https://github ...

  6. 从零开始打造一个Android 3D立体旋转容器

    本文地址,转载请注明 http://blog.csdn.net/mr_immortalz/article/details/51918560  github 代码下载地址 :https://github ...

  7. android标题栏添加按钮_2019最新Android常用开源库总结(持续更新,建议收藏)

    热文导读|   点击标题阅读 如何才能成为优秀的架构师? 23种设计模式及案例整理分享(建议收藏) 程序员的35个坏习惯,你有几条? 作者:欢子-3824 来源:https://blog.csdn.n ...

  8. android悬浮窗按钮在哪,android——悬浮窗控件Toast

    前端 后端 Toast 是一个悬浮窗控件,在不影响用户操作的条件下,按动按钮显示悬浮窗. 不同的是,Toast不支持点击事件,需要函数调用.所以要在外部函数初始化我们按钮的 控件,当我们的对象V创建出 ...

  9. android工程的建立,第一个Android项目HelloWorld的建立及剖析

    1.建立一个简单的Hello World程序 步骤1:启动Eclipse,选择 New->Other,如下图所示 步骤2:在出现的窗口中选择Android Project,如下图所示: 步骤3: ...

最新文章

  1. python线性整数规划求解_实例详解:用Python解决整数规划问题!
  2. 简述原型模型的特点_软件工程简答题答案 第五版
  3. 读书感想--list/BSS等等
  4. 【PAT乙级】1038 统计同成绩学生 (20 分)
  5. Linux下的OpenSSL编程
  6. spring 加载java类_在Spring中基于Java类进行配置的完整步骤
  7. Canvas的save和restore
  8. HTTP状态码的类别
  9. lte接口流程图_请画出LTE系统的组网图及标注接口。
  10. 容器编排技术 -- 使用Vagrant本地运行Kubernetes
  11. 在Powerbuilder中播放Flash动画
  12. UI素材|最全面的移动端 UI KIT 模板
  13. 2021-2025年中国伊维菌素原料药行业市场供需与战略研究报告
  14. mysql备份怎么锁库_MySQL锁(一)全局锁:如何作全库的逻辑备份?
  15. bind + DNSCrypt 实现安全加密转发,避免DNS污染
  16. 中文繁简互换以及获取中文字符串首字母
  17. 第39级台阶--递归
  18. 父亲母亲-山里老房子
  19. 华三防火墙Reth链路冗余技术
  20. 制作一个小黄鸭转圈跳舞的页面。

热门文章

  1. android 调起第三方地图并添加标注
  2. 创建自己的Maven库
  3. VScode格式化时 标签属性不换行
  4. created at mysql类型_mysql中Invalid default value for 'created_at'问题汗血宝马
  5. JavaScript BOM对象和DOM对象(DHTML)
  6. 稻盛和夫(日本世界著名实业家、哲学家)
  7. 鸿蒙墅宛建筑装饰,自然界城墅380平米港式风格装修效果图
  8. 无线收发器如何实现超长前导码发送?
  9. 单纤光纤收发器a与b怎么放?如何使用光纤收发器的AB端?
  10. 骁龙870和麒麟980哪个好 骁龙870和麒麟980对比哪个更强