最近因为项目需求,需要写一个自定义的扩散加震动效果,废了一下午的时间,终于给他搞出来了,结果第二天需求就改掉了..
不说那么多题外话了,直接撸代码~
首先是XML文件:
<com.example.XXXXX.sxt.view.customview.DiffuseView
android:id="@+id/customCall"
android:layout_width="@dimen/x120"
android:layout_height="@dimen/x120"
app:diffuse_coreRadius="100"
app:diffuse_maxWidth="80"
app:diffuse_width="10"
android:layout_centerInParent="true"/>
属性&方法
属性名 | java方法 | 作用 |
---|---|---|
diffuse_color | setColor(int colorId) | 设置扩散圆颜色 |
diffuse_coreColor | setCoreColor(int colorId) | 设置中心圆颜色 |
diffuse_coreImage | setCoreImage(int imageId) | 设置中心圆图片 |
diffuse_coreRadius | setCoreRadius(int radius) | 设置中心圆半径 |
diffuse_maxWidth | setMaxWidth(int maxWidth) | 设置最大扩散宽度 |
diffuse_width | setDiffuseWidth(int width) | 设置扩散圆宽度,值越小越宽 |
Java代码(直接拷贝成一个类就OK~):
/**
* Created by HXY on 2018/9/25.
* Be used for : 首页电话按钮背景view,自定义扩散背景
*/
public class DiffuseView extends View {
/** 扩散圆圈颜色 */
private int mColor = getResources().getColor(R.color.colorPink);
/** 圆圈中心颜色 */
private int mCoreColor = getResources().getColor(R.color.colorPink);
/** 圆圈中心图片 */
private Bitmap mBitmap;
/** 中心圆半径 */
private float mCoreRadius = 150;
/** 扩散圆宽度 */
private int mDiffuseWidth = 3;
/** 最大宽度 */
private Integer mMaxWidth = 255;
/** 是否正在扩散中 */
private boolean mIsDiffuse = false;
// 透明度集合
private List<Integer> mAlphas = new ArrayList<>();
// 扩散圆半径集合
private List<Integer> mWidths = new ArrayList<>();
private Paint mPaint;
public DiffuseView(Context context) {
this(context, null);
}
public DiffuseView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public DiffuseView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DiffuseView, defStyleAttr, 0);
mColor = a.getColor(R.styleable.DiffuseView_diffuse_color, mColor);
mCoreColor = a.getColor(R.styleable.DiffuseView_diffuse_coreColor, mCoreColor);
mCoreRadius = a.getFloat(R.styleable.DiffuseView_diffuse_coreRadius, mCoreRadius);
mDiffuseWidth = a.getInt(R.styleable.DiffuseView_diffuse_width, mDiffuseWidth);
mMaxWidth = a.getInt(R.styleable.DiffuseView_diffuse_maxWidth, mMaxWidth);
int imageId = a.getResourceId(R.styleable.DiffuseView_diffuse_coreImage, -1);
if(imageId != -1) mBitmap = BitmapFactory.decodeResource(getResources(), imageId);
a.recycle();
}
private void init() {
mPaint = new Paint();
mPaint.setAntiAlias(true);
mAlphas.add(255);
mWidths.add(0);
}
@Override
public void invalidate() {
if(hasWindowFocus()){
super.invalidate();
}
}
@Override
public void onDraw(Canvas canvas) {
// 绘制扩散圆
mPaint.setColor(mColor);
for (int i = 0; i < mAlphas.size(); i++) {
// 设置透明度
Integer alpha = mAlphas.get(i);
mPaint.setAlpha(alpha);
// 绘制扩散圆
Integer width = mWidths.get(i);
canvas.drawCircle(getWidth() / 2, getHeight() / 2, mCoreRadius + width, mPaint);
if(alpha > 0 && width < mMaxWidth){
mAlphas.set(i, alpha - 3);
mWidths.set(i, width + 1);
}
}
// 判断当扩散圆扩散到指定宽度时添加新扩散圆
if (mWidths.get(mWidths.size() - 1) == mMaxWidth / 1) {
mAlphas.add(255);
mWidths.add(0);
}
// 超过3个扩散圆,删除最外层
if(mWidths.size() >= 2){
mWidths.remove(0);
mAlphas.remove(0);
}
// 绘制中心圆及图片
mPaint.setAlpha(255);
mPaint.setColor(mCoreColor);
canvas.drawCircle(getWidth() / 2, getHeight() / 2, mCoreRadius, mPaint);
if(mBitmap != null){
Bitmap newBitmap = BitmapUtils.changeSize(mBitmap, 150, 150);
canvas.drawBitmap(newBitmap, getWidth() / 2 - newBitmap.getWidth() / 2
, getHeight() / 2 - newBitmap.getHeight() / 2, mPaint);
}
if(mIsDiffuse){
invalidate();
}
}
/**
* 开始扩散
*/
public void start() {
mIsDiffuse = true;
invalidate();
}
/**
* 停止扩散
*/
public void stop() {
mIsDiffuse = false;
}
/**
* 是否扩散中
*/
public boolean isDiffuse(){
return mIsDiffuse;
}
/**
* 设置扩散圆颜色
*/
public void setColor(int colorId){
mColor = colorId;
}
/**
* 设置中心圆颜色
*/
public void setCoreColor(int colorId){
mCoreColor = colorId;
}
/**
* 设置中心圆图片
*/
public void setCoreImage(int imageId){
mBitmap = BitmapFactory.decodeResource(getResources(), imageId);
}
/**
* 设置中心圆半径
*/
public void setCoreRadius(int radius){
mCoreRadius = radius;
}
/**
* 设置扩散圆宽度(值越小宽度越大)
*/
public void setDiffuseWidth(int width){
mDiffuseWidth = width;
}
/**
* 设置最大宽度
*/
public void setMaxWidth(int maxWidth){
mMaxWidth = maxWidth;
}
}
外部调用:
DiffuseView mDiffuseView = (DiffuseView) findViewById(R.id.diffuseView);
mDiffuseView.start(); // 开始扩散
mDiffuseView.stop();// 停止扩散
其实在网上看到类似代码的时候还是有很多坑的,比如如果直接给这个自定义控件添加图片的话,我们是不能给图片添加动效的,因为直接设置图片的时候是bitmap对象.所以我的做法是用一个 RelativeLayout 去作为外层布局,里面放我们的自定义控件,同时添加一个 ImageView
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="@dimen/x130"
android:background="@mipmap/home">
<com.example.XXXXX.sxt.view.customview.DiffuseView
android:id="@+id/customCall"
android:layout_width="@dimen/x120"
android:layout_height="@dimen/x120"
app:diffuse_coreRadius="100"
app:diffuse_maxWidth="80"
app:diffuse_width="10"
android:layout_centerInParent="true"/>
<ImageView
android:id="@+id/call"
android:layout_width="@dimen/x40"
android:layout_height="@dimen/x40"
android:background="@mipmap/phone"
android:layout_centerInParent="true"
android:clickable="true"/>
</RelativeLayout>
这样就能达成我们期望的效果,直接在代码里面设置动画就可以了.但是真正这样做了之后又遇到一个问题,只要我们开启了动画之后,我们的自定义控件就不能正常绘制,只有由默认加载状态切换一下其他页面,再切换回来之后才能正常绘制出我们想要的效果.具体什么原因现在暂时没有搞明白,知道的同学可以下方评论留言一下~
解决方法就是开启两个线程,分别去控制自定义的效果和图片的动画效果,代码如下:
//开始播放电话按钮相关动画
private void startAni(final ImageView call) {
Timer timer = new Timer();
TimerTask task = new TimerTask() {
@Override
public void run() {
// TODO Auto-generated method stub
getActivity().runOnUiThread(new Runnable() {
public void run() {
//开启电话颤动效果
setAnimation(call);
}
});
}
};
TimerTask task2 = new TimerTask() {
@Override
public void run() {
// TODO Auto-generated method stub
getActivity().runOnUiThread(new Runnable() {
public void run() {
//开启背景扩散效果
customCall.start();
}
});
}
};
//执行开启任务
timer.schedule(task, 0, 2500);
timer.schedule(task2, 0, 100);
}
/**
* 设置自定义动画
*/
private void setAnimation(ImageView imageView){
AnimatorSet animatorSet = new AnimatorSet();
Animator anim1 = ObjectAnimator.ofFloat(imageView, "rotation", 0f, 30f);
anim1.setDuration(200);
Animator anim2 = ObjectAnimator.ofFloat(imageView, "rotation", 30f, -15f);
anim1.setDuration(150);
Animator anim3 = ObjectAnimator.ofFloat(imageView, "rotation", -15f, 00f);
anim1.setDuration(150);
animatorSet.playSequentially(anim1,anim2,anim3);
animatorSet.start();
}
暂时解决,达成需求~
--------------------- 本文借鉴于 Airsaid 的CSDN 博客 ,原文地址请点击:https://blog.csdn.net/airsaid/article/details/52683193?utm_source=copy