实现了一个有趣的小东西:使用自定义View绘图,一边画线,画出的线条渐渐变淡,直到消失。效果如下图所示:

用属性动画或者渐变填充(Shader)可以做到一笔一笔的变化,但要想一笔渐变(手指不抬起边画边渐隐),没在Android中找到现成的API可用。所以,自己做了一个。

基本的想法是这样的:

  • 在View的onTouchEvent中记录触摸点,生成一条一条的线LineElement,放在一个List中。给每个LineElement配置一个Paint实例。
  • 在onDraw中绘制线段。
  • 变换LineElement的Paint实例的Alpha值。
  • 根据Alpha值重组线段列表

别的不说了,上代码:

package com.example.disappearinglines;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.support.annotation.NonNull;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View; import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator; public class DisappearingDoodleView extends View {
final static String TAG = "DoodleView";
class LineElement {
static final public int ALPHA_STEP = 5;
static final public int SUBPATH_DIMENSION = 8;
public LineElement(){
mPaint = new Paint();
mPaint.setARGB(255, 255, 0, 0);
mPaint.setAntiAlias(true);
mPaint.setStrokeWidth(16);
mPaint.setStrokeCap(Paint.Cap.BUTT);
mPaint.setStyle(Paint.Style.STROKE);
}
public LineElement(Paint paint){
mPaint = paint;
} public void setPaint(Paint paint){
mPaint = paint;
} public void setAlpha(int alpha){
mPaint.setAlpha(alpha);
} public float mStartX = -1;
public float mStartY = -1;
public float mEndX = -1;
public float mEndY = -1;
public Paint mPaint;
} private LineElement mCurrentLine = null;
private List<LineElement> mLines = null; private long mElapsed = 0;
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg){
DisappearingDoodleView.this.invalidate();
}
}; public DisappearingDoodleView(Context context){
super(context);
} public DisappearingDoodleView(Context context, AttributeSet attrs){
super(context, attrs);
} @Override
protected void onDraw(Canvas canvas){
mElapsed = SystemClock.elapsedRealtime();
if(mLines != null) {
for (LineElement e : mLines) {
if(e.mStartX < 0 || e.mEndY < 0) continue;
canvas.drawLine(e.mStartX, e.mStartY, e.mEndX, e.mEndY, e.mPaint);
}
compactPaths();
}
} @Override
public boolean onTouchEvent(MotionEvent event){
float x = event.getX();
float y = event.getY(); int action = event.getAction();
if(action == MotionEvent.ACTION_UP){// end one line after finger release
mCurrentLine.mEndX = x;
mCurrentLine.mEndY = y;
mCurrentLine = null;
invalidate();
return true;
} if(action == MotionEvent.ACTION_DOWN){
mCurrentLine = new LineElement();
addToPaths(mCurrentLine); mCurrentLine.mStartX = x;
mCurrentLine.mStartY = y;
return true;
} if(action == MotionEvent.ACTION_MOVE) {
mCurrentLine.mEndX = x;
mCurrentLine.mEndY = y;
mCurrentLine = new LineElement();
addToPaths(mCurrentLine); mCurrentLine.mStartX = x;
mCurrentLine.mStartY = y;
} if(mHandler.hasMessages(1)){
mHandler.removeMessages(1);
}
Message msg = new Message();
msg.what = 1;
mHandler.sendMessageDelayed(msg, 0); return true;
} private void addToPaths(LineElement element){
if(mLines == null) {
mLines = new ArrayList<LineElement>() ;
} mLines.add(element);
} public void compactPaths(){ int size = mLines.size();
int index = size - 1;
if(size == 0) return;
int baseAlpha = 255 - LineElement.ALPHA_STEP;
int itselfAlpha;
LineElement line;
for(; index >=0 ; index--, baseAlpha -= LineElement.ALPHA_STEP){
line = mLines.get(index);
itselfAlpha = line.mPaint.getAlpha();
if(itselfAlpha == 255){
if(baseAlpha <= 0){
++index;
break;
}
line.setAlpha(baseAlpha);
}else{
itselfAlpha -= LineElement.ALPHA_STEP;
if(itselfAlpha <= 0){
++index;
break;
}
line.setAlpha(itselfAlpha);
}
} if(index >= size){
// all sub-path should disappear
mLines = null;
}
else if(index >= 0){
//Log.i(TAG, "compactPaths from " + index + " to " + (size - 1));
mLines = mLines.subList(index, size);
}else{
// no sub-path should disappear
} long interval = 40 - SystemClock.elapsedRealtime() + mElapsed;
if(interval < 0) interval = 0;
Message msg = new Message();
msg.what = 1;
mHandler.sendMessageDelayed(msg, interval);
}
}

这个示例还可以添加一些效果,比如让线条一边变淡一边变细。

目前还有一些问题,线条粗的话,可以明显看到线段与线段之间有缝隙或裂口,哪位想到怎么优化,可以告诉我,非常感谢。^_^。

Android绘图之渐隐动画的更多相关文章

  1. Android群英传》读书笔记 (3) 第六章 Android绘图机制与处理技巧 + 第七章 Android动画机制与使用技巧

    第六章 Android绘图机制与处理技巧 1.屏幕尺寸信息屏幕大小:屏幕对角线长度,单位“寸”:分辨率:手机屏幕像素点个数,例如720x1280分辨率:PPI(Pixels Per Inch):即DP ...

  2. Android绘图机制(四)——使用HelloCharts开源框架搭建一系列炫酷图表,柱形图,折线图,饼状图和动画特效,抽丝剥茧带你认识图表之美

    Android绘图机制(四)--使用HelloCharts开源框架搭建一系列炫酷图表,柱形图,折线图,饼状图和动画特效,抽丝剥茧带你认识图表之美 这里为什么不继续把自定义View写下去呢,因为最近项目 ...

  3. 第三章 Android绘图机制与处理技巧

    1.屏幕尺寸信息 屏幕大小:屏幕对角线长度,单位“寸”:分辨率:手机屏幕像素点个数,例如720x1280分辨率:PPI(Pixels Per Inch):即DPI(Dots Per Inch),它是对 ...

  4. Android View的滑动 动画

    [scrollTo/scrollBy] //控件内的文字会移动,但是控件本身不会移动,而且移动到控件之外之后,文字也就看不见了 if(v.equals(button2)){ button2.scrol ...

  5. Android之滑屏动画和自定义控件

    滑屏动画 在Android系统中,通过手势识别切换界面时,通常会在界面切换时加入动画,以提高用户的体验效果,这种动画一般都采用平移动画,下一个界面进入时,上一个界面移除屏幕. 图中标识的均为左上角坐标 ...

  6. 论文第5章:Android绘图平台的实现

    面向移动设备的矢量绘图平台设计与实现 Design and Implementation of Mobile Device-oriented Vector Drawing Platform 引用本论文 ...

  7. android 绘图之Canvas,Paint类

    Canvas,Paint 1.在android 绘图但中经常要用到Canvas和Paint类,Canvas好比是一张画布,上面已经有你想绘制图画的轮廓了,而Paint就好比是画笔,就要给Canvas进 ...

  8. android中设置Animation 动画效果

    在 Android 中, Animation 动画效果的实现可以通过两种方式进行实现,一种是 tweened animation 渐变动画,另一种是 frame by frame animation ...

  9. Android使用XML做动画UI

    在Android应用程序,使用动画效果,能带给用户更好的感觉.做动画可以通过XML或Android代码.本教程中,介绍使用XML来做动画.在这里,介绍基本的动画,如淡入,淡出,旋转等. 效果: htt ...

随机推荐

  1. uva----11729 Commando war (突击战争)

    G Commando War Input: Standard Input Output: Standard Output “Waiting for orders we held in the wood ...

  2. JAVA 正则 Pattern 和 Matcher

    java.util.regex是一个用正则表达式所订制的模式来对字符串进行匹配工作的类库包. 1.简介:  java.util.regex是一个用正则表达式所订制的模式来对字符串进行匹配工作的类库包. ...

  3. Checked 和 UnChecked 异常 的使用场合

    异常的概念  任何的异常都是Throwable类(为何不是接口??),并且在它之下包含两个子类Error / Exception,而Error仅在当在Java虚拟机中发生动态连接失败或其它的定位失败的 ...

  4. hdu5876 Sparse Graph(补图最短路 bfs)

    题目链接:hdu5876 Sparse Graph 详见代码.. #include<cstdio> #include<cstring> #include<algorith ...

  5. web页面动态加载UserControl,并调用用户控件中的方法来初始化控件

    1,HTML页 头部注册: <%@ Register Src="~/WorkLog/WorkLogNewV1/UserControl/CeShiBu.ascx" TagPre ...

  6. JS比较好用的一些方法搜集

    JS比较好用的一些方法搜集 Math.ceil(x) -- 返回大于等于数字参数的最小整数(取整函数),对数字进行上舍入 Math.floor(x)--返回小于等于数字参数的最大整数,对数字进行下舍入 ...

  7. 支持新版chrome,用webstorm编译形成css和sourcemap,调试sass和less源文件(转)

    旧版的chrome有个support for sass,但是新版chrome没有这个功能了.看到网上提供的方法比较多,也很乱,旧版新版的都有.而且不能指定自己所需要的路径. 所以就做了下改版. sas ...

  8. ios基础篇(六)——UITextView的常用方法及技巧

    上篇说到了UITextField,我们先来说说UITextView和UITextField的不同: UITextView支持多行输入; UITextFiled只支持单行; UITextView没有pl ...

  9. linux 安装 php

    1.  libxml2安装           tar -zxvf libxml2-2.6.26.tar.gz cd libxml2-2.6.26 ./configure  --prefix=/usr ...

  10. 所思所想 关于asp.net界面业务分离

    1.体会:使用ASP.NET控件来做前段是有很大的局限性的 2.使用拼接HTML的方式来写代码虽然不符合模式,但是有很大的灵活性 3.如果使用拼接字符串的方式来生成前台的代码,使用NV的话完全可以实现 ...