天天看點

模仿實作360桌面水晶球式的一鍵清理特效

半年前,鄙人想搞一下 android一鍵清理的特效,于是乎研究了一下市面上各個産品的界面效果,發現360桌面的水晶球效果很細膩,是以就試着模仿了一下。今天翻閱以前寫的代碼,剛好看到了它,是以就想把它分享出來,供大家拍磚。

模仿實作360桌面水晶球式的一鍵清理特效

先上一副截圖,看看原版的效果:

模仿實作360桌面水晶球式的一鍵清理特效

其實360的這個界面效果,最細膩的地方在于它在水面的最上層,覆寫了一張具有立體效果的水面圖檔,這樣使用者看起來,這個水晶球的浮動效果就很有立體的感覺了。

要想實作這個動畫特效,以我的思路看來,隻需要解決兩個關鍵點就可以了:

1.利用clipdrawable對背景帶有顔色的圖檔進行切割

2.對最上層的那張立體圖檔,進行縮放和上下平移,這樣就可以達到跟背景圖檔融合為一體的效果了。

稍候我會把這個demo工程繼續上傳到csdn上來,是以項目中用到的素材圖檔,是如何配置設定的,大家可以把demo下載下傳下來後,自己研究一下,還是有幾個細節是需要向360學習的。

下面我們開始講解代碼:

首先,我先把項目中用到的xml檔案貼出來,供大家參考:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

    <ImageView
        android:id="@+id/image_back"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:background="@drawable/taskmanager_background"
        android:src="@drawable/task_killer_full_clip"/>
    
    <ImageView
        android:id="@+id/image_waterlevel"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:src="@drawable/taskmanager_circle_full_waterlevel"/>
 
    <ImageView
        android:id="@+id/image_top"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:src="@drawable/taskmanager_water_top"/>    
</RelativeLayout>
           

id号為image_waterlevel的控件,就是用來承載那張立體水面的空間。

id号為image_backd的空間,就是帶有背景顔色的空間,android:src 所引用的drawable就是一個自定義的clipdrawable的xml,下面我把它的xml貼出來:

<?xml version="1.0" encoding="utf-8"?>  
<clip xmlns:android="http://schemas.android.com/apk/res/android"   
    android:drawable="@drawable/taskmanager_circle_full"  
    android:clipOrientation="vertical"
    android:gravity="bottom">  
</clip>  
           

這裡的幾個參數需要注意一下,drawable引用的資源檔案就是res下面真實的圖檔資源。clipOrientation屬性指的是圖檔的切割是按什麼方向切割的,這裡我們選擇的是垂直方向切割。gravity屬性指的是圖檔從哪個位置開始切割,我們這裡選擇從底部開始切割。

用到的幾個xml主要就這些,另外還有兩個clipdrawable的xml,不過屬性設定都一樣,隻是引用的背景顔色圖檔不一樣。大家可以從demo工程中看到。

下面我們開始講解代碼:

package com.example.taskkillerdemo;

import java.util.Timer;
import java.util.TimerTask;

import android.app.Activity;
import android.graphics.drawable.ClipDrawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageView;

public class MainActivity extends Activity implements OnClickListener{

	final int CLEANTASK = 1;
	final int CLEANNUMBER = 10;//水面每次變化是時,clipdrawable變化的程度,大家可以自己随意修改這個數值,用來控制水面變化的速度。
	
	ImageView imageSrc;//用來顯示液體顔色的控件
	ImageView waterLevel;//用來顯示睡眠的那張圖檔,有了這張圖檔的修飾,整個水晶球的水面才有了立體的效果。這個設計真的好巧妙
	final int TOTALLEVEL = 10000;//clipdrawable的切割範圍,clipdrawable的切割範圍是從0到10000
	int actualLevel;//表示clipdrawable實時的切割數值,在初始化方法中,我們會将它指派為8000
	float radius;//液體顔色那張圖檔的半徑
	
	private Timer timer;
	private TimerTask task;
	
	private Handler hand;
	
	boolean isDown = true;//這個變量用來判斷水面現在是出于下降的狀态還是上升的狀态
	boolean isOver;//timer每次循環都會根據它來判斷handler是否已經把上次的動畫效果處理完,如果還沒有處理完,那就不向handler發送新的消息
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		/**
		 * 這個handler用來跟timer互動,實時更改水晶球水面的變化和背景顔色
		 */
		hand = new Handler(){
			@Override
			public void handleMessage(Message msg) {
				// TODO Auto-generated method stub
				super.handleMessage(msg);
				if(msg.what == CLEANTASK){
					//每次開始處理動畫時,把isover設定成false,這樣timer就不會再發新的消息過來了。
					isOver = false;
					
					//下面的代碼用來判斷水面是否已經下降到了底部或者回升到了最大值
					if(isDown){
						actualLevel = (actualLevel-CLEANNUMBER)>=0?(actualLevel-CLEANNUMBER):0;
						if(actualLevel==0){
							//當下降到最底部時,isDown設為false,後面handler在接受到消息時,就開始處理上升的動畫
							isDown = false;
						}
					}else{
						actualLevel = (actualLevel+CLEANNUMBER)<=8000?(actualLevel+CLEANNUMBER):8000;
						if(actualLevel==8000){
							//當回升到最上層時,isDown設為true,本次動畫過程徹底結束,timer控制器登出。
							isDown = true;
							task.cancel();
							task = null;
							timer.cancel();
							timer = null;
						}
					}
					
					//根據clipdrawable切割的範圍,來更換背景顔色和立體水面的顔色。
					if(actualLevel>=6000){
						imageSrc.setImageResource(R.drawable.task_killer_full_clip);
						waterLevel.setImageResource(R.drawable.taskmanager_circle_full_waterlevel);
					}else if(actualLevel<6000&&actualLevel>4000){
						imageSrc.setImageResource(R.drawable.task_killer_mid_clip);
						waterLevel.setImageResource(R.drawable.taskmanager_circle_mid_waterlevel);
					}else{
						imageSrc.setImageResource(R.drawable.task_killer_low_clip);
						waterLevel.setImageResource(R.drawable.taskmanager_circle_min_waterlevel);
					}
					
					ClipDrawable clip = (ClipDrawable) imageSrc.getDrawable();
					clip.setLevel(actualLevel);
					waterLevelChange();
					
					//每次動畫處理結束後,把isover設定成true,這樣timer又會發新的消息過來
					isOver = true;
				}
			}
		};
		
		initData();
		initView();
	}

	/**
	 * 初始化資料
	 */
	void initData(){
		actualLevel = 8000;
		isOver = true;
	}
	
	/**
	 * 初始化視圖
	 */
	void initView(){
		imageSrc = (ImageView)findViewById(R.id.image_back);
		waterLevel = (ImageView)findViewById(R.id.image_waterlevel);
		
		imageSrc.setOnClickListener(this);
		
		//這裡擷取clipdrawable的執行個體,擷取背景圖檔的半徑值,然後對clipdrawable進行切割,對水面進行平移縮放
		ClipDrawable clip = (ClipDrawable) imageSrc.getDrawable();
		radius = clip.getIntrinsicHeight()/2;
		clip.setLevel(actualLevel);
		waterLevelChange();
	}
	
	/**
	 * 計算水面的那張圖檔,縮放的大小和上下平移的大小,運用勾股定理來計算
	 */
	void waterLevelChange(){
		//預設水面那張圖檔處在中間位置,那麼她随着水面上下移動的距離應該為:
		float transY = ((5000-actualLevel)/5000.0f)*radius;
		//根據上面的計算值,然後根據勾股定理,可以計算出水面在水準方向的縮放比例:
		float scaleX= 0.0f;
		if(actualLevel!=5000){
			scaleX = (float) (Math.sqrt(radius*radius - transY*transY)/radius);
		}else{
			scaleX = 1f;
		}
		//這裡設定水面圖檔的上下平移值和水準縮放值
		waterLevel.setTranslationY(transY);
		waterLevel.setScaleX(scaleX);
	}

	@Override
	public void onClick(View v) {
		//每次點選時,看看現在水晶球是否處于動畫狀态,如果不處于,才進行新的動畫處理。這裡的判斷規則可以随意規定,我就用最簡單粗暴的方式來實作了。
		if(actualLevel==8000){
			timer = new Timer();
			task = new TimerTask() {  
	            @Override  
	            public void run() {
	            	//timer每次循環都會根據它來判斷handler是否已經把上次的動畫效果處理完,如果還沒有處理完,那就不向handler發送新的消息
	            	if(isOver){
	                	Message msg = new Message();
	                	msg.what = CLEANTASK;
	                	hand.sendMessage(msg);
	            	}
	            }  
	        };
	        timer.schedule(task, 0, 1);
		}
	}
}
           

用到的代碼就這麼一點點,因為寫的比較倉促,代碼結構不是很好,沒有進行封裝拆分,還望大家諒解。分享出來,就是希望能夠在動畫效果方面,給大家提供一種思路,如果文章裡有誤人子弟的地方,請及時告訴我,鄙人定會面壁思過,阿彌陀佛!!!

模仿實作360桌面水晶球式的一鍵清理特效

本來想上傳一張GIF的demo圖檔,可是每次上傳之後,都是顯示為靜态圖檔。好費解。。。。

下面是demo的下載下傳位址:

http://download.csdn.net/detail/pringlee2011/5946421