自定義View實現雨點灑落效果
最近事情不多,想着寫點東西,又不知道寫什麼好,實現個小效果,娛樂一下,順帶自己也再熟練下自定義view的繪製的相關知識,不說廢話,先上圖:
一、前期分析
很明顯,上面場景是又一個居中的TextView以及一個個小紅點和散開的圓圈構成。在點擊上面文本框的時候文本框出現抖動併產生一個小紅點,下紅點沿預定路徑下落,在下落到指定高度時顯示散開的波浪效果,同時伴有透明度的變化。好了,效果分析完,咱開始代碼實現吧。
二、文本框抖動的實現
文本框抖動很簡單,一個平移效果的動畫,加上循環差值器,xml如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="#fff">
<TextView
android:id="@+id/tv"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginTop="30dp"
android:layout_marginLeft="30dp"
android:layout_marginRight="30dp"
android:background="#f60"
android:clickable="true"
android:textColor="#fff"
android:gravity="center"
android:text="hahaha" />
<com.example.test.CustomView
android:id="@+id/v"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</LinearLayout>
private TextView tv;
tv=(TextView) findViewById(R.id.tv);
final TranslateAnimation animation = (TranslateAnimation)AnimationUtils.loadAnimation(this, R.anim.animation);
tv.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
tv.startAnimation(animation);
}
});
animation.xml:
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="500"
android:fromXDelta="0"
android:interpolator="@anim/cycle"
android:toXDelta="10"
>
</translate>
cycle.xml:
<cycleInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
android:cycles="5" />
這個實現很簡單,大家看一下應該都能明白,我們繼續。
三、圓點生成以及散開效果
ok,我們的圓點生成器已經完成,我們要製作圓點了,動手之前,我們再次觀察一下,我**每點擊一次**就**生成一個**小圓點,而且小圓點是按照**自己的軌跡**掉落下來,最後自己散開。
聰明的小夥伴應該已經猜到了,每個小圓點應該都是獨立的,按照面向對象的思想,每個小圓點應該都做爲一個獨立的對象存在,那我們就先來創建小圓點的對象吧。
class Circle{
//x軸的 的座標
int x;
//y軸的座標
int y;
//畫筆的透明度值
int alpha;
//掉落後圓環的寬度
float width;
//圓環的半徑
float radius;
//繪製散開圓環的畫筆
Paint paint;
}
在產生小圓點以及小圓點散開的過程中,我們就是要不斷的繪製我們的界面,這裏呢,我們自定義一個View來實現這個效果。
public class CustomView extends View {
//畫小圓點的畫筆
private Paint paint;
private List<Circle> list = new ArrayList<CustomView.Circle>();
//x初始值
//圓環的畫筆
private Paint Circlepaint;
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(Color.RED);
Circlepaint = new Paint(paint);
Circlepaint.setStyle(Paint.Style.STROKE);
}
@Override
protected void onDraw(Canvas canvas) {
for(Circle circle:list){
if(circle.alpha<15){
circle.alpha = 0;
}
circle.paint.setAlpha(circle.alpha);
if(circle.y >= dp2px(300)){
circle.paint.setStrokeWidth(circle.width);
canvas.drawCircle(circle.x, dp2px(300), circle.radius, circle.paint);
}else{
canvas.drawCircle(circle.x, circle.y, circle.radiusSamll, paint);
}
}
}
//外部刷新view
public void refresh(int y){
int x = dp2px(150);
int cishu = new Random().nextInt(4);
switch (cishu) {
case 0:
x-=dp2px(30);
break;
case 1:
x+=dp2px(30);
break;
case 2:
x-=dp2px(10);
break;
case 3:
x+=dp2px(10);
break;
}
Circle circle = new Circle();
circle.x = x;
circle.y = y;
circle.radius = dp2px(60f);
circle.alpha = 255;
circle.radiusSamll = dp2px(10);
circle.width = dp2px(60f);
circle.paint = Circlepaint;//畫圓的paint
list.add(circle);
invalidate();
}
private List<Circle> tempList = new ArrayList<CustomView.Circle>();
@Override
public void computeScroll() {
for(Circle circle:list){
if(circle.y <dp2px(300)){
circle.y+=dp2px(8);
invalidate();
}else{
if(circle.alpha >= 0){
circle.radius+=dp2px(28);
circle.alpha-=10;
circle.width-=dp2px(15);
if(circle.width<0){
circle.width = 0;
circle.radius = 0;
}
invalidate();
}else{
//將alpha值小於0的對象保存在臨時集合中
tempList.add(circle);
}
}
}
//從集合中去除看不見的對象
list.removeAll(tempList);
}
class Circle{
//x軸的座標
int x;
//y軸的座標
int y;
//畫筆的透明度值
int alpha;
//掉落後圓環的寬度
float width;
//散落下圓環的半徑
float radius;
//小圓點的半徑
float radiusSamll;
//繪製散開圓環的畫筆
Paint paint;
}
public int dp2px(float value){
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value, getResources().getDisplayMetrics());
}
}
private TextView tv;
private CustomView cv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
cv = (CustomView) findViewById(R.id.v);
tv=(TextView) findViewById(R.id.tv);
final TranslateAnimation animation = (TranslateAnimation)AnimationUtils.loadAnimation(this, R.anim.animation);
tv.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
tv.startAnimation(animation);
// 刷新 界面
cv.refresh(0);
}
});
}
其中核心就是每點擊一次就創建一個圓點對象,並存儲在集合中,在每次onDraw調用時,遍歷集合中每一個對象繪製在界面上,invalidate方法會觸發computeScroll方法,在computeScroll方法調用時,遍歷判斷每個對象的透明值是否小於0,即是否可見,如果不可見,我們就將其從集合中移除出去,注意集合在遍歷時不允許進行增刪操作,所以這裏我們先放進一個臨時集合中,在遍歷結束之後統一移除。
四、總結
自定義view最主要的就是根據狀態賦予不同的變量值,從而不斷的顯示不同的效果,Paint,Canvas是兩個很強大的類,有很多有趣的功能等着我們探索,加油吧。