教你快速實(shí)現(xiàn)Android動態(tài)模糊效果
前言
雅虎天氣的界面上滑的時(shí)候背景圖片會跟著移動,最重要的是背景圖片會根據(jù)手指上下移動的距離來進(jìn)行不同程度的模糊,感覺甚為驚奇,畢竟大家都知道,在Android平臺上進(jìn)行模糊渲染是一個(gè)相當(dāng)耗CPU也相當(dāng)耗時(shí)的操作,一旦處理不好,卡頓是在所難免的。
一般來說,考慮到效率,渲染一張圖片最好的方法是使用OpenGL
,其次是使用C++/C,使用Java代碼是最慢的。但是Android推出RenderScript
之后,我們就有了新的選擇,測試表明,使用RenderScript
的渲染效率和使用C/C++不相上下,但是使用RenderScript
卻比使用JNI
簡單地多!同時(shí),Android團(tuán)隊(duì)提供了RenderScript
的支持庫,使得在低版本的Android平臺上也能使用。
不過在使用RenderScript
之前,對于模糊一張圖片,需要注意的是,我們應(yīng)該盡量不要使用原尺寸分辨率的圖片,最好將圖片縮小比例,這小渲染的效率要高一些。
動態(tài)模糊的實(shí)現(xiàn)
如何使用RenderScript
來模糊一張圖片呢?廢話不多說,先上核心代碼:
public class BlurBitmap { /** * 圖片縮放比例 */ private static final float BITMAP_SCALE = 0.4f; /** * 最大模糊度(在0.0到25.0之間) */ private static final float BLUR_RADIUS = 25f; /** * 模糊圖片的具體方法 * * @param context 上下文對象 * @param image 需要模糊的圖片 * @return 模糊處理后的圖片 */ public static Bitmap blur(Context context, Bitmap image) { // 計(jì)算圖片縮小后的長寬 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)核對象 RenderScript rs = RenderScript.create(context); // 創(chuàng)建一個(gè)模糊效果的RenderScript的工具對象 ScriptIntrinsicBlur blurScript = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs)); // 由于RenderScript并沒有使用VM來分配內(nèi)存,所以需要使用Allocation類來創(chuàng)建和分配內(nèi)存空間。 // 創(chuàng)建Allocation對象的時(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對象的輸入內(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 }
代碼做了簡單的注釋以幫助理解,如果需要詳細(xì)了解,可以查閱官方文檔
然后,我們可以看一下模糊前和模糊后的效果對比:
將圖片模糊后,接下來要考慮的是怎么實(shí)現(xiàn)動態(tài)模糊效,有一點(diǎn)需要注意的是,即使我們使用了RenderScript
這種高效的渲染方式,但是在實(shí)際測試中,渲染一張500*700分辨率的PNG格式圖片,在我的Pro 6手機(jī)上,仍然需要50ms左右的時(shí)間,顯然如果使用上面的代碼進(jìn)行實(shí)時(shí)渲染的話,會造成界面嚴(yán)重的卡頓。
既然實(shí)時(shí)渲染這條路走不通,那么就需要我們另辟蹊徑了,我這里可以提供一種方法:先將圖片進(jìn)行最大程度的模糊處理,再將原圖放置在模糊后的圖片上面,通過不斷改變原圖的透明度(Alpha值)來實(shí)現(xiàn)動態(tài)模糊效果。
簡單的代碼如下:
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滑動事件 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滑動事件 */ 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>
效果如下:
怎么樣?是不是很簡單的樣子?只需要調(diào)用模糊處理方法,并在SeekBar的滑動監(jiān)聽里面調(diào)用原圖像的setAlpha()
方法,來實(shí)現(xiàn)動態(tài)模糊效果。
你以為這樣就完了?不不不,我們的目的并不是這么單純,哦,不對,并不是這么簡單。還記得文章開頭的時(shí)候說了嗎?我們的終極目的是要簡單地模仿一下雅虎天氣的界面效果。
仿雅虎天氣界面
有了上面的基礎(chǔ),就可以很容易地模仿雅虎天氣的界面效果。簡單來說,在上面制作出的效果基礎(chǔ)上,有以下兩點(diǎn)需要注意的地方:
需要要監(jiān)聽滑動事件,然后再將背景圖片調(diào)用setTop()
方法,將圖片向上平移一段距離。
要向上平移圖片,還需要手動增加圖片的高度,不然圖片向上平移后,底部就會有留白。設(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)容后,基本就可以模仿出雅虎天氣的首頁了。
結(jié)合第一個(gè)例子的demo,效果如下:
總結(jié)
以上就是本文的全部內(nèi)容了,實(shí)現(xiàn)后的效果是不是很贊呢?感興趣的朋友快快自己動手操作起來吧,希望本文對大家開發(fā)Android能有所幫助。
- Android 動態(tài)高斯模糊效果教程
- Android實(shí)現(xiàn)動態(tài)高斯模糊效果
- Android圖片特效:黑白特效、圓角效果、高斯模糊
- Android中實(shí)現(xiàn)布局背景模糊化處理的方法
- Android模糊處理實(shí)現(xiàn)圖片毛玻璃效果
- Android模糊處理簡單實(shí)現(xiàn)毛玻璃效果
- Android關(guān)于Glide的使用(高斯模糊、加載監(jiān)聽、圓角圖片)
- Android調(diào)用系統(tǒng)拍照裁剪圖片模糊的解決方法
- Android 實(shí)現(xiàn)圖片模糊、高斯模糊、毛玻璃效果的三種方法
- Android動態(tài)模糊效果的快速實(shí)現(xiàn)方法
相關(guān)文章
詳解Android中visibility屬性VISIBLE、INVISIBLE、GONE的區(qū)別
在Android開發(fā)中,大部分控件都有visibility這個(gè)屬性,其屬性有3個(gè)分別為“visible ”、“invisible”、“gone”。主要用來設(shè)置控制控件的顯示和隱藏。本文就詳細(xì)的講解一下。2016-12-12用Android?studio實(shí)現(xiàn)簡易計(jì)算器功能
這篇文章主要為大家詳細(xì)介紹了用Android?studio實(shí)現(xiàn)簡易計(jì)算器功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-05-05Android中自定義加載樣式圖片的具體實(shí)現(xiàn)
想實(shí)現(xiàn)下面這張圖中的自定義加載樣式,其實(shí)很簡單,首先我們需要的布局組件有ProcessBar和TextView,下面是布局文件的代碼2014-04-04Android顏色處理SweepGradient掃描及梯度渲染示例
這篇文章主要為大家介紹了Android顏色處理SweepGradient掃描渲染及梯度渲染示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06Android TextView中文字通過SpannableString設(shè)置屬性用法示例
這篇文章主要介紹了Android TextView中文字通過SpannableString設(shè)置屬性用法,結(jié)合實(shí)例形式分析了TextView控件中SpannableString類相關(guān)屬性的使用技巧,需要的朋友可以參考下2016-08-08Android實(shí)現(xiàn)簡易計(jì)算功能
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)簡易計(jì)算功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-06-06超簡單Android集成華為HMS Scankit 掃碼SDK實(shí)現(xiàn)掃一掃二維碼
這篇文章主要介紹了超簡單Android集成華為HMS Scankit 掃碼SDK實(shí)現(xiàn)掃一掃二維碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-03-03