教你快速實(shí)現(xiàn)Android動(dòng)態(tài)模糊效果
前言
雅虎天氣的界面上滑的時(shí)候背景圖片會(huì)跟著移動(dòng),最重要的是背景圖片會(huì)根據(jù)手指上下移動(dòng)的距離來(lái)進(jìn)行不同程度的模糊,感覺甚為驚奇,畢竟大家都知道,在Android平臺(tái)上進(jìn)行模糊渲染是一個(gè)相當(dāng)耗CPU也相當(dāng)耗時(shí)的操作,一旦處理不好,卡頓是在所難免的。
一般來(lái)說(shuō),考慮到效率,渲染一張圖片最好的方法是使用OpenGL,其次是使用C++/C,使用Java代碼是最慢的。但是Android推出RenderScript之后,我們就有了新的選擇,測(cè)試表明,使用RenderScript的渲染效率和使用C/C++不相上下,但是使用RenderScript卻比使用JNI簡(jiǎn)單地多!同時(shí),Android團(tuán)隊(duì)提供了RenderScript的支持庫(kù),使得在低版本的Android平臺(tái)上也能使用。
不過(guò)在使用RenderScript之前,對(duì)于模糊一張圖片,需要注意的是,我們應(yīng)該盡量不要使用原尺寸分辨率的圖片,最好將圖片縮小比例,這小渲染的效率要高一些。
動(dòng)態(tài)模糊的實(shí)現(xiàn)
如何使用RenderScript來(lái)模糊一張圖片呢?廢話不多說(shuō),先上核心代碼:
public class BlurBitmap {
/**
* 圖片縮放比例
*/
private static final float BITMAP_SCALE = 0.4f;
/**
* 最大模糊度(在0.0到25.0之間)
*/
private static final float BLUR_RADIUS = 25f;
/**
* 模糊圖片的具體方法
*
* @param context 上下文對(duì)象
* @param image 需要模糊的圖片
* @return 模糊處理后的圖片
*/
public static Bitmap blur(Context context, Bitmap image) {
// 計(jì)算圖片縮小后的長(zhǎng)寬
int width = Math.round(image.getWidth() * BITMAP_SCALE);
int height = Math.round(image.getHeight() * BITMAP_SCALE);
// 將縮小后的圖片做為預(yù)渲染的圖片。
Bitmap inputBitmap = Bitmap.createScaledBitmap(image, width, height, false);
// 創(chuàng)建一張渲染后的輸出圖片。
Bitmap outputBitmap = Bitmap.createBitmap(inputBitmap);
// 創(chuàng)建RenderScript內(nèi)核對(duì)象
RenderScript rs = RenderScript.create(context);
// 創(chuàng)建一個(gè)模糊效果的RenderScript的工具對(duì)象
ScriptIntrinsicBlur blurScript = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
// 由于RenderScript并沒有使用VM來(lái)分配內(nèi)存,所以需要使用Allocation類來(lái)創(chuàng)建和分配內(nèi)存空間。
// 創(chuàng)建Allocation對(duì)象的時(shí)候其實(shí)內(nèi)存是空的,需要使用copyTo()將數(shù)據(jù)填充進(jìn)去。
Allocation tmpIn = Allocation.createFromBitmap(rs, inputBitmap);
Allocation tmpOut = Allocation.createFromBitmap(rs, outputBitmap);
// 設(shè)置渲染的模糊程度, 25f是最大模糊度
blurScript.setRadius(BLUR_RADIUS);
// 設(shè)置blurScript對(duì)象的輸入內(nèi)存
blurScript.setInput(tmpIn);
// 將輸出數(shù)據(jù)保存到輸出內(nèi)存中
blurScript.forEach(tmpOut);
// 將數(shù)據(jù)填充到Allocation中
tmpOut.copyTo(outputBitmap);
return outputBitmap;
}
}
完成上面的代碼后,需要在app的gradle文件中添加如下的支持:
defaultConfig {
......
renderscriptTargetApi 19
renderscriptSupportModeEnabled true
}
代碼做了簡(jiǎn)單的注釋以幫助理解,如果需要詳細(xì)了解,可以查閱官方文檔
然后,我們可以看一下模糊前和模糊后的效果對(duì)比:

將圖片模糊后,接下來(lái)要考慮的是怎么實(shí)現(xiàn)動(dòng)態(tài)模糊效,有一點(diǎn)需要注意的是,即使我們使用了RenderScript這種高效的渲染方式,但是在實(shí)際測(cè)試中,渲染一張500*700分辨率的PNG格式圖片,在我的Pro 6手機(jī)上,仍然需要50ms左右的時(shí)間,顯然如果使用上面的代碼進(jìn)行實(shí)時(shí)渲染的話,會(huì)造成界面嚴(yán)重的卡頓。
既然實(shí)時(shí)渲染這條路走不通,那么就需要我們另辟蹊徑了,我這里可以提供一種方法:先將圖片進(jìn)行最大程度的模糊處理,再將原圖放置在模糊后的圖片上面,通過(guò)不斷改變?cè)瓐D的透明度(Alpha值)來(lái)實(shí)現(xiàn)動(dòng)態(tài)模糊效果。
簡(jiǎn)單的代碼如下:
public class MainActivity extends AppCompatActivity {
/**
* 原始圖片控件
*/
private ImageView mOriginImg;
/**
* 模糊后的圖片控件
*/
private ImageView mBluredImage;
/**
* 進(jìn)度條SeekBar
*/
private SeekBar mSeekBar;
/**
* 顯示進(jìn)度的文字
*/
private TextView mProgressTv;
/**
* 透明度
*/
private int mAlpha;
/**
* 原始圖片
*/
private Bitmap mTempBitmap;
/**
* 模糊后的圖片
*/
private Bitmap mFinalBitmap;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 初始化視圖
initViews();
// 獲取圖片
mTempBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.dayu);
mFinalBitmap = BlurBitmap.blur(this, mTempBitmap);
// 填充模糊后的圖像和原圖
mBluredImage.setImageBitmap(mFinalBitmap);
mOriginImg.setImageBitmap(mTempBitmap);
// 處理seekbar滑動(dòng)事件
setSeekBar();
}
/**
* 初始化視圖
*/
private void initViews() {
mBluredImage = (ImageView) findViewById(R.id.activity_main_blured_img);
mOriginImg = (ImageView) findViewById(R.id.activity_main_origin_img);
mSeekBar = (SeekBar) findViewById(R.id.activity_main_seekbar);
mProgressTv = (TextView) findViewById(R.id.activity_main_progress_tv);
}
/**
* 處理seekbar滑動(dòng)事件
*/
private void setSeekBar() {
mSeekBar.setMax(100);
mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
mAlpha = progress;
mOriginImg.setAlpha((int) (255 - mAlpha * 2.55));
mProgressTv.setText(String.valueOf(mAlpha));
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
}
}
xml布局文件代碼如下:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent"> <FrameLayout android:layout_width="match_parent" android:layout_weight="1" android:layout_height="0dp"> <ImageView android:id="@+id/activity_main_blured_img" android:scaleType="centerCrop" android:src="@drawable/dayu" android:layout_width="match_parent" android:layout_height="match_parent"/> <ImageView android:id="@+id/activity_main_origin_img" android:scaleType="centerCrop" android:layout_width="match_parent" android:layout_height="match_parent"/> </FrameLayout> <LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="80dp"> <SeekBar android:layout_marginTop="@dimen/activity_vertical_margin" android:id="@+id/activity_main_seekbar" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="16dp" android:layout_marginRight="16dp"/> <TextView android:id="@+id/activity_main_progress_tv" android:text="0" android:textSize="24sp" android:layout_gravity="center" android:layout_width="wrap_content" android:layout_height="wrap_content"/> </LinearLayout> </LinearLayout>
效果如下:

怎么樣?是不是很簡(jiǎn)單的樣子?只需要調(diào)用模糊處理方法,并在SeekBar的滑動(dòng)監(jiān)聽里面調(diào)用原圖像的setAlpha()方法,來(lái)實(shí)現(xiàn)動(dòng)態(tài)模糊效果。
你以為這樣就完了?不不不,我們的目的并不是這么單純,哦,不對(duì),并不是這么簡(jiǎn)單。還記得文章開頭的時(shí)候說(shuō)了嗎?我們的終極目的是要簡(jiǎn)單地模仿一下雅虎天氣的界面效果。
仿雅虎天氣界面
有了上面的基礎(chǔ),就可以很容易地模仿雅虎天氣的界面效果。簡(jiǎn)單來(lái)說(shuō),在上面制作出的效果基礎(chǔ)上,有以下兩點(diǎn)需要注意的地方:
需要要監(jiān)聽滑動(dòng)事件,然后再將背景圖片調(diào)用setTop()方法,將圖片向上平移一段距離。
要向上平移圖片,還需要手動(dòng)增加圖片的高度,不然圖片向上平移后,底部就會(huì)有留白。設(shè)置圖片高度的核心代碼如下:
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); Display display = wm.getDefaultDisplay(); Point point = new Point(); display.getSize(point); // 獲取到ImageView的高度 int height = point.y; ViewGroup.LayoutParams params = imageView.getLayoutParams(); params.width = ViewGroup.LayoutParams.MATCH_PARENT; // 將ImageView的高度增加100 params.height = height + 100; // 應(yīng)用更改設(shè)置 imageView.requestLayout();
完成上面兩點(diǎn)的內(nèi)容后,基本就可以模仿出雅虎天氣的首頁(yè)了。
結(jié)合第一個(gè)例子的demo,效果如下:

總結(jié)
以上就是本文的全部?jī)?nèi)容了,實(shí)現(xiàn)后的效果是不是很贊呢?感興趣的朋友快快自己動(dòng)手操作起來(lái)吧,希望本文對(duì)大家開發(fā)Android能有所幫助。
- Android 動(dòng)態(tài)高斯模糊效果教程
- Android實(shí)現(xiàn)動(dòng)態(tài)高斯模糊效果
- Android圖片特效:黑白特效、圓角效果、高斯模糊
- Android中實(shí)現(xiàn)布局背景模糊化處理的方法
- Android模糊處理實(shí)現(xiàn)圖片毛玻璃效果
- Android模糊處理簡(jiǎn)單實(shí)現(xiàn)毛玻璃效果
- Android關(guān)于Glide的使用(高斯模糊、加載監(jiān)聽、圓角圖片)
- Android調(diào)用系統(tǒng)拍照裁剪圖片模糊的解決方法
- Android 實(shí)現(xiàn)圖片模糊、高斯模糊、毛玻璃效果的三種方法
- Android動(dòng)態(tài)模糊效果的快速實(shí)現(xiàn)方法
相關(guān)文章
Android實(shí)現(xiàn)可輸入數(shù)據(jù)的彈出框
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)可輸入數(shù)據(jù)的彈出框,文章提供了兩種方式,示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-01-01
Android實(shí)現(xiàn)圖片轉(zhuǎn)高斯模糊以及高斯模糊布局
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)圖片轉(zhuǎn)高斯模糊的方法,以及高斯模糊布局,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-08-08
Android JNI 調(diào)用時(shí)緩存字段和方法ID示例
這篇文章主要介紹了Android JNI 調(diào)用時(shí)緩存字段和方法ID示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-07-07
Android 仿抖音的評(píng)論列表的UI和效果的實(shí)現(xiàn)代碼
抖音是一款音樂創(chuàng)意短視頻社交軟件,此app已在android各大應(yīng)用商店和app store 上線。下面小編給大家?guī)?lái)了Android 仿抖音的評(píng)論列表的UI和效果的實(shí)現(xiàn)代碼,感興趣的朋友參考下吧2018-03-03
Android?Navigation重建Fragment問題分析及解決
這篇文章主要介紹了Android?Navigation重建Fragment問題分析及解決,文章通過(guò)圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-09-09
Android 開發(fā)中使用Linux Shell實(shí)例詳解
這篇文章主要介紹了Android 開發(fā)中使用Linux Shell實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下2017-03-03

