安卓动画: 参照博客:http://blog.csdn.net/lmj623565791/article/details/38067475
逐帧动画 补间动画 属性动画
逐帧动画:Drawable Animation ,FrameAnimation 对一个ImageView 不停的设置新的图片
补间动画:ViewAnimation ,也叫Tweened Animation 平移,缩放,透明,旋转 有局限性.
属性动画:PropertyAnimation 补间动画的升级 :通过动画的方式改变对象的属性
属性动画与补间动画最大的区别就是,控件通过属性动画改变位置后,控件的位置会随着改变,而补间动画控件还是在原地
相关类:(类似于ImageLoader的框架)
动画的执行类 :ObjectAnimator,ValueAnimator
AnimatorSet:动画集合
AnimatorInflater:加载文件夹为animator的XML文件
TypeEvaluator:类型估值,只要用于设置动画操作属性的值
TimeInterpolator:时间插值器(也叫插补器). 设置动画加速减速.变化率
属性动画执行流程:动画的执行类来设置动画操作的对象的属性.持续时间,开始和结束的属性值,时间插值等,然后系统会根据设置的参数,动态的变化对象的属性.
PropertyName 有四个
translationXY,rotation,alpha,scaleXY
translationXY和scaleXY只有后缀加XY的方法,不表明XY方向的画会出现找不到方法的错误. 注意XY必须大写.我们将他们作为字符串当做参数传入方法里,由刚才的错误可以发现这个字符串是作为一个方法名来运行的.
通过方法
ObjectAnimator.ofFloat.setDuration.start
简单实现
也可以通过
PropertyValuesHolder 属性持有者这个属性来设置同时播放俩种动画
方法
ObjectAnimator.ofPropertyValuesHolder.setDuration.start这个方法参数是一个可变数组
示例代码:
case R.id.image:
//TODO
ObjectAnimator//3个参数:作用对象 动画名 动画需要配置的值:可变数组
.ofFloat(mImage, "translationX", 30f,90f)
// .ofFloat(mImage, "translationX", 90f,30f)
.setDuration(1500)
.start();
之前的补间动画有AnimationSet实现多种动画组合播放,而属性动画也有这个功能就是AnimatorSet
AnimatorSet通过Play().with().after().before()通过这些方法来实现 动画和动画的先后顺序,以及同时播放功能.
修改背景色:闪光灯效果 方法名:backgroundColor字符串
ObjectAnimator.ofInt 至于颜色则是通过可变参数实现
关于属性动画插值器的笔记
Interpolator,在安卓1.0的时候就已经出现Interpolator接口了,可以翻译成插值器,用于计算补间动画的变化速率. 而在安卓3.0的时候引入了属性动画,同时新增了TimeInterpolator接口,这就使得
Interpolator的实现类可以直接拿到属性动画中使用.
主要的实现类
AccelerateDecelerateInterpolator 在动画开始与介绍的地方速率改变比较慢,在中间的时候加速
AccelerateInterpolator 在动画开始的地方速率改变比较慢,然后开始加速
AnticipateInterpolator 开始的时候向后然后向前甩
AnticipateOvershootInterpolator 开始的时候向后然后向前甩一定值后返回最后的值
BounceInterpolator 动画结束的时候弹起
CycleInterpolator 动画循环播放特定的次数,速率改变沿着正弦曲线
DecelerateInterpolator 在动画开始的地方快然后慢
LinearInterpolator 以常量速率改变
OvershootInterpolator 向前甩一定值后再回到原来位置
使用:一个方法 animator.setInterpolator(new BounceInterpolator());
实例代码:
ObjectAnimator animator = ObjectAnimator
.ofFloat(mImage, "rotationY", 0.0f, 360f);
animator.setInterpolator(new BounceInterpolator());
animator.setDuration(5000)
.start();
属性动画课堂笔记
ObjectAnimator属性动画特点:动画效果会改变控件的位置,且开启动画的是动画对象,而不是控件对象.
注意:
如果你想让你的App与众不同,请用自定义控件,
吊炸天+狂拽=android动画+自定义动画
Android3.0之后才出现的新特性.最低兼容API11
在XML定义动画类属性,浮点型小数,直接写小数即可,不用再带f
提示:控件位移的参数在XML文件里有所不同,不过更简单,不用再特意去定义参照物的属性,直接根据值,区分俩种方式.
一种:一整个屏幕为参照物,在XML文件属性定义值是int%p
一种:以控件自身大小为参照物,
使用方式:
示例代码
Animator animator1 = AnimatorInflater.loadAnimator(MainActivity.this, R.animator.set);
animator1.setTarget(mImage);
animator1.start();
ValueAnimator 实现动画
无需设置操作的属性,这就是和ObjectAnimator的区别
好处:不需要操作对象的属性,一定要有getter,setter方法,你可以根据当前动画的计算值,来操作任何属性
示例代码
private void Vertical() {//heightPixels - mImage.getHeight()
final ValueAnimator animator = ValueAnimator.ofFloat(0,300 );
animator.setTarget(mImage);
animator.setDuration(2000).start();
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
mImage.setTranslationY((float)animator.getAnimatedValue());
}
});
}
/*********************************/
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
int duration = 500;
private ImageView mImageJia;
private ImageView mImageIc1;
private ImageView mImageIc2;
private RelativeLayout mRela;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
mImageIc1 = (ImageView) findViewById(R.id.imageIc1);
mImageIc2 = (ImageView) findViewById(R.id.imageIc2);
mImageJia = (ImageView) findViewById(R.id.imageJia);
mImageJia.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.imageJia:
Toast.makeText(MainActivity.this, "222", Toast.LENGTH_SHORT).show();
StartAnimation();
break;
}
}
private boolean isChecked = false;
private void StartAnimation() {
if (isChecked == false) {
// PropertyValuesHolder p3 = PropertyValuesHolder.ofFloat("TranslationX", 0f, 150f);
ObjectAnimator animator1 = ObjectAnimator.ofFloat(mImageIc1, "TranslationX", 0f, 150f);
ObjectAnimator animator2 = ObjectAnimator.ofFloat(mImageIc2, "TranslationX", 0f, 300f);
ObjectAnimator animator3 = ObjectAnimator.ofFloat(mImageJia, "Rotation", 0f, 360f);
//添加自由落体效果插值器
animator1.setInterpolator(new BounceInterpolator());
animator2.setInterpolator(new BounceInterpolator());
animator3.setInterpolator(new BounceInterpolator());
//启动动画
animator1.setDuration(duration).start();
animator2.setDuration(duration).start();
animator3.setDuration(duration).start();
animator3.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
mImageJia.setImageResource(R.drawable.jian);
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
isChecked = true;
} else {
ObjectAnimator animator1 = ObjectAnimator.ofFloat(mImageIc1, "TranslationX", 150f,0f);
ObjectAnimator animator2 = ObjectAnimator.ofFloat(mImageIc2, "TranslationX", 300f,0f );
ObjectAnimator animator3 = ObjectAnimator.ofFloat(mImageJia, "Rotation", 0f, 720f);
//添加自由落体效果插值器
// animator1.setInterpolator(new CycleInterpolator(1));
// animator2.setInterpolator(new CycleInterpolator(5));
// animator3.setInterpolator(new CycleInterpolator(10));
//启动动画
animator1.setDuration(duration).start();
animator2.setDuration(duration).start();
animator3.setDuration(duration).start();
animator3.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
mImageJia.setImageResource(R.drawable.jia);
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
isChecked = false;
}
}
}
补充:
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private ImageView mImage;
private Button mButAlpha;
private Button mButScale;
private Button mButTranslatatin;
private Button mBuRotation;
private Button mButSR;
private Button mButRT;
private Button mButAlphaRepeat;
private Button mBuBGcolor;
private Button mButPaoWuxian;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
mImage = (ImageView) findViewById(R.id.image);
mButAlpha = (Button) findViewById(R.id.butAlpha);
mButAlpha.setOnClickListener(this);
mButScale = (Button) findViewById(R.id.butScale);
mButScale.setOnClickListener(this);
mButTranslatatin = (Button) findViewById(R.id.butTranslatatin);
mButTranslatatin.setOnClickListener(this);
mBuRotation = (Button) findViewById(R.id.buRotation);
mBuRotation.setOnClickListener(this);
mButSR = (Button) findViewById(R.id.butSR);
mButSR.setOnClickListener(this);
mButRT = (Button) findViewById(R.id.butRT);
mButRT.setOnClickListener(this);
mButAlphaRepeat = (Button) findViewById(R.id.butAlphaRepeat);
mButAlphaRepeat.setOnClickListener(this);
mBuBGcolor = (Button) findViewById(R.id.buBGcolor);
mBuBGcolor.setOnClickListener(this);
mButPaoWuxian = (Button) findViewById(R.id.butPaoWuxian);
mButPaoWuxian.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.butAlpha://透明
ObjectAnimator
.ofFloat(mImage, "alpha", 0.0f, 1.0f)
.setDuration(1000)
.start();
break;
case R.id.butScale://缩放 同时播放俩种 XY并存的都可以这样写
ObjectAnimator
.ofFloat(mImage, "scaleX", 0.0f, 1.0f)
.setDuration(1000)
.start();
break;
case R.id.butTranslatatin://位移
PropertyValuesHolder translationX = PropertyValuesHolder.ofFloat("translationX", 20f, 80f);
PropertyValuesHolder translationY = PropertyValuesHolder.ofFloat("translationY", 20f, 80f);
ObjectAnimator
.ofPropertyValuesHolder(mImage, translationX, translationY)
.setDuration(1000)
.start();
break;
case R.id.buRotation://旋转
ObjectAnimator
.ofFloat(mImage,"rotationY",0.0f,360f)
.setDuration(1000)
.start();
break;
case R.id.butSR://先播放缩放动画,完成后播放旋转动画
AnimatorSet animatorSetGroup1 = new AnimatorSet();
ObjectAnimator objectAnimatorScaleX1 = ObjectAnimator.ofFloat(mImage, "scaleX", 0f, 1f);
ObjectAnimator objectAnimatorScaleY1 = ObjectAnimator.ofFloat(mImage, "scaleY", 0f, 1f);
ObjectAnimator objectAnimatorRotateX1 = ObjectAnimator.ofFloat(mImage, "rotationX", 0f, 360f);
ObjectAnimator objectAnimatorRotateY1 = ObjectAnimator.ofFloat(mImage, "rotationY", 0f, 360f);
animatorSetGroup1.setDuration(1000);
animatorSetGroup1.play(objectAnimatorScaleX1).with(objectAnimatorScaleY1)
.before(objectAnimatorRotateX1).before(objectAnimatorRotateY1);
animatorSetGroup1.start();
break;
case R.id.butRT://先播放旋转动画,完成后播放位移动画
AnimatorSet animatorSetGroup2 = new AnimatorSet();
ObjectAnimator objectAnimatorTranslate2 = ObjectAnimator.ofFloat(mImage, "translationX", 0f, 500f);
ObjectAnimator objectAnimatorRotateX2 = ObjectAnimator.ofFloat(mImage, "rotationX", 0f, 360f);
ObjectAnimator objectAnimatorRotateY2 = ObjectAnimator.ofFloat(mImage, "rotationY", 0f, 360f);
animatorSetGroup2.setDuration(1000);
animatorSetGroup2.play(objectAnimatorTranslate2).after(objectAnimatorRotateX2)
.after(objectAnimatorRotateY2);
animatorSetGroup2.start();
break;
case R.id.butAlphaRepeat://重复的透明度动画 闪烁效果
//TODO
ObjectAnimator objectAnimator2 = ObjectAnimator.ofFloat(mImage, "alpha", 0f, 1f);
objectAnimator2.setDuration(500);
objectAnimator2.setRepeatCount(3);
objectAnimator2.start();
break;
case R.id.buBGcolor://背景颜色不断改变
//TODO
ObjectAnimator objectAnimatorBg = ObjectAnimator.ofInt(mImage, "backgroundColor", Color.BLUE, Color.YELLOW, Color.RED);
objectAnimatorBg.setDuration(3000);
objectAnimatorBg.start();
break;
case R.id.butPaoWuxian://背景颜色不断改变
//TODO
parabola();
break;
default:
break;
}
}
/**
* 抛物线动画
*/
@RequiresApi(api = Build.VERSION_CODES.HONEYCOMB)
public void parabola()
{
ValueAnimator valueAnimator = new ValueAnimator();
valueAnimator.setDuration(3000);
valueAnimator.setObjectValues(new PointF(0, 0));
valueAnimator.setInterpolator(new LinearInterpolator());
valueAnimator.setEvaluator(new TypeEvaluator<PointF>()
{
@Override
public PointF evaluate(float fraction, PointF startValue,
PointF endValue)
{
/**x方向200px/s ,则y方向0.5 * 200 * t**/
PointF point = new PointF();
point.x = 200 * fraction * 3;
point.y = 0.5f * 200 * (fraction * 3) * (fraction * 3);
return point;
}
});
valueAnimator.start();
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener()
{
@Override
public void onAnimationUpdate(ValueAnimator animation)
{
PointF point = (PointF) animation.getAnimatedValue();
mImage.setX(point.x);
mImage.setY(point.y);
}
});
}
}
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/butAlpha"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="透明" />
<Button
android:id="@+id/butScale"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="缩放" />
<Button
android:id="@+id/butTranslatatin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="位移" />
<Button
android:id="@+id/buRotation"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="旋转" />
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/butSR"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="先缩放后旋转"
android:textSize="14sp" />
<Button
android:id="@+id/butRT"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="先旋转后位移"
android:textSize="14sp" />
<Button
android:id="@+id/butAlphaRepeat"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="透明重放:闪烁"
android:textSize="14sp" />
<Button
android:id="@+id/buBGcolor"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="背景色改变"
android:textSize="14sp" />
</LinearLayout>
<Button
android:id="@+id/butPaoWuxian"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="抛物线动画"
android:textSize="14sp" />
<ImageView
android:id="@+id/image"
android:layout_width="200dp"
android:layout_height="250dp"
android:layout_gravity="center"
android:layout_marginTop="200dp"
android:src="@drawable/cat" />
</LinearLayout>
</layout>
时隔俩年,现在加一些
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="weiyi"
android:text="开始位移" />
<Button
android:id="@+id/biaozhi"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="标志" />
</LinearLayout>
mViewBinding.biaozhi.layout(mViewBinding.biaozhi.getWidth(), mViewBinding.biaozhi.getHeight(), mViewBinding.biaozhi.getWidth()*2, mViewBinding.biaozhi.getHeight()*2);
layout是改变view在布局的大小和位置
让他向右移,只需要改变右和下方向的距离,我们可以 根据结束的位置来写最终位置的俩个点(左,上),(右,下)
ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 400);
int width = mViewBinding.biaozhi.getWidth();
int height = mViewBinding.biaozhi.getHeight();
valueAnimator.setDuration(3000);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int curValue = (int) animation.getAnimatedValue();
mViewBinding.biaozhi.layout(curValue, height + curValue, width + curValue, height * 2 + curValue);
}
});
valueAnimator.start();
做一个loading
public class MyView extends ImageView {
/**
* 距上
*/
private int mTop;
/**
* 当前图片索引 也就是第几张开始
*/
private int mCurSrc = 0;
/**
* 图片总数
*/
private int mSrccount = 3;
public MyView(Context context) {
this(context, null);
}
public MyView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 100, 0);
valueAnimator.setRepeatMode(ValueAnimator.RESTART);
valueAnimator.setRepeatCount(ValueAnimator.INFINITE);
valueAnimator.setDuration(2000);
valueAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int animatedValue = (int) animation.getAnimatedValue();
setTop(mTop - animatedValue);
}
});
valueAnimator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
switch (mCurSrc % mSrccount) {
case 0:
setImageDrawable(getResources().getDrawable(R.drawable.cat));
break;
case 1:
setImageDrawable(getResources().getDrawable(R.drawable.dog));
break;
case 2:
setImageDrawable(getResources().getDrawable(R.drawable.pig));
break;
default:
break;
}
mCurSrc++;
}
});
valueAnimator.start();
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
mTop = top;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
}
}
<com.as.zqfjavasample.formal.customviews.MyView
android:layout_width="@dimen/dp50"
android:layout_height="@dimen/dp50"
android:layout_marginTop="@dimen/dp50"
android:scaleType="fitXY" />
Region
public class MyView extends View {
private Paint mPaint;
public MyView(Context context) {
this(context, null);
}
public MyView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mPaint = new Paint();
mPaint.setAntiAlias(true);//设置抗锯齿
mPaint.setColor(Color.BLACK);//设置画笔颜色
mPaint.setDither(true);// 防止抖动
mPaint.setStyle(Paint.Style.STROKE); // 边框
mPaint.setStrokeWidth(10);//宽度
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Rect mRect = new Rect(100, 100, 500, 300);
Region mRegion = new Region(mRect);
drawRegion(canvas, mRegion, mPaint);
}
private void drawRegion(Canvas canvas, Region mRegion, Paint mPaint) {
RegionIterator iterator = new RegionIterator(mRegion);
Rect rectF = new Rect();
while (iterator.next(rectF)) {
canvas.drawRect(rectF, mPaint);
}
}
}
类似普通的矩形,但是既然要通过Region来包装,那么这里一定是有不同寻常的Api
它可以用来画俩个区域重叠的地方
public class MyView extends View {
private Paint mPaint;
public MyView(Context context) {
this(context, null);
}
public MyView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mPaint = new Paint();
mPaint.setAntiAlias(true);//设置抗锯齿
mPaint.setColor(Color.BLACK);//设置画笔颜色
mPaint.setDither(true);// 防止抖动
mPaint.setStyle(Paint.Style.FILL); // 边框
mPaint.setStrokeWidth(10);//宽度
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Path mPath = new Path();
mPath.addOval(new RectF(100, 100, 500, 500), Path.Direction.CW);
Region mRegion = new Region();
Rect mRect = new Rect(100, 100, 500, 200);
mRegion.setPath(mPath, new Region(mRect));
drawRegion(canvas, mRegion, mPaint);
}
private void drawRegion(Canvas canvas, Region mRegion, Paint mPaint) {
RegionIterator iterator = new RegionIterator(mRegion);
Rect rectF = new Rect();
while (iterator.next(rectF)) {
canvas.drawRect(rectF, mPaint);
}
}
}
union函数
区域相交 合并矩形
Region region = new Region(10, 10, 510, 410);
region.union(new Rect(10,10,310,610));
drawRegion(canvas, region, mPaint);
op方法
public class MyView extends View {
private Paint mPaint;
public MyView(Context context) {
this(context, null);
}
public MyView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mPaint = new Paint();
mPaint.setAntiAlias(true);//设置抗锯齿
mPaint.setColor(Color.BLACK);//设置画笔颜色
mPaint.setDither(true);// 防止抖动
mPaint.setStyle(Paint.Style.STROKE); // 边框
mPaint.setStrokeWidth(10);//宽度
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Rect rect1 = new Rect(100, 100, 400, 200);
Rect rect2 = new Rect(200, 0, 300, 300);
Region region1 = new Region(rect1);
Region region2 = new Region(rect2);
Region region = new Region();
// region.op(region1, region2, Region.Op.DIFFERENCE);
// region.op(region1, region2, Region.Op.INTERSECT);
// region.op(region1, region2, Region.Op.REPLACE);
// region.op(region1, region2, Region.Op.UNION);
// region.op(region1, region2, Region.Op.XOR);
region.op(region1, region2, Region.Op.REVERSE_DIFFERENCE);
canvas.drawRect(rect1, mPaint);
canvas.drawRect(rect2, mPaint);
mPaint.setStyle(Paint.Style.FILL);
drawRegion(canvas, region, mPaint);
}
private void drawRegion(Canvas canvas, Region mRegion, Paint mPaint) {
RegionIterator iterator = new RegionIterator(mRegion);
Rect rectF = new Rect();
while (iterator.next(rectF)) {
canvas.drawRect(rectF, mPaint);
}
}
}
canvas.save/restore
当我们对canvas(画布)进行操作后就需要对画布状态进行保存(save),恢复(restore)
举个栗子
canvas.drawCircle(100, 100, 50, mPaint);
canvas.translate(200, 200);
canvas.drawCircle(100, 100, 50, mPaint);
默认的中心点是:0,0 所有100,100的圆在左上角,当我们使用translate之后画布换了位置,中心点到了200,200,所以100,100画在了右下角
canvas.drawCircle(100, 100, 50, mPaint);
canvas.save();
canvas.translate(200, 200);
canvas.restore();
mPaint.setColor(Color.RED);
canvas.drawCircle(110, 110, 50, mPaint);
可以看到这样画布恢复了,中心点又回去了
小栗子:
canvas.drawCircle(500, 500, 400, mPaint);
mPaint.setStyle(Paint.Style.FILL); // 实心
for (int i = 0; i < 7; i++) {
canvas.save();
canvas.rotate(60 * i, 500, 500);
canvas.drawLine(500, 500, 500, 0, mPaint);
canvas.restore();
}
// 看看最终的中心点在哪
canvas.drawPoint(10, 10, mPaint);
这个有个问题,每次打开App 然后退到后台不杀死再打开就会变成Fill 类型的
这个问题找到了原因了就是每次界面可见时,就会调用自定义View 的OnDraw ,所以