欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android 動(dòng)態(tài)高斯模糊效果教程

 更新時(shí)間:2016年09月12日 09:59:30   作者:Iamxiarui  
本文主要介紹Android 動(dòng)態(tài)高斯模糊效果教程,這里整理了詳細(xì)的資料及實(shí)例實(shí)現(xiàn)代碼,有興趣的小伙伴可以參考下

寫(xiě)在前面

最近一直在做畢設(shè)項(xiàng)目的準(zhǔn)備工作,考慮到可能要用到一個(gè)模糊的效果,所以就學(xué)習(xí)了一些高斯模糊效果的實(shí)現(xiàn)。比較有名的就是 FastBlur 以及它衍生的一些優(yōu)化方案,還有就是今天要說(shuō)的RenderScript 。

因?yàn)檫@東西是現(xiàn)在需要才去學(xué)習(xí)的,所以關(guān)于一些圖像處理和渲染問(wèn)題就不提了。不過(guò)在使用的過(guò)程中確實(shí)能感受到,雖然不同的方案都能實(shí)現(xiàn)相同的模糊效果,但是效率差別真的很大。

本篇文章實(shí)現(xiàn)的高斯模糊是根據(jù)下面這篇文章學(xué)習(xí)的,先推薦一下。本文內(nèi)容與其內(nèi)容差不多,只是稍微講的詳細(xì)一點(diǎn),并修改了代碼中部分實(shí)現(xiàn)邏輯和細(xì)節(jié)上的處理。不過(guò)主體內(nèi)容不變,所以選擇哪篇文章去學(xué)都是一樣的。

下面就來(lái)看一下,如何去實(shí)現(xiàn)這樣的高斯模糊效果。

簡(jiǎn)單聊聊 Renderscript

因?yàn)樾Ч膶?shí)現(xiàn)是基于 Renderscript 的,所以有必要先來(lái)了解一下。

從它的官方文檔來(lái)看,說(shuō)的很是玄乎。我們只需要知道一點(diǎn)就好了:

RenderScript is a framework for running computationally intensive tasks at high performance on Android.
Renderscript 是 Android 平臺(tái)上進(jìn)行高性能計(jì)算的框架。
既然是高性能計(jì)算,那么說(shuō)明 RenderScript 對(duì)圖像的處理非常強(qiáng)大,所以用它來(lái)實(shí)現(xiàn)高斯模糊還是比較好的選擇。

那么如何使用它呢?從官方文檔中可以看到,如果需要在 Java 代碼中使用 Renderscript 的話,就必須依賴 android.renderscript 或者android.support.v8.renderscript 中的 API 。既然有 API 那就好辦多了。

下面簡(jiǎn)單說(shuō)一下使用的步驟,這也是官方文檔中的說(shuō)明:

  1. 首先需要通過(guò) Context 創(chuàng)建一個(gè) Renderscript ;
  2. 其次通過(guò)創(chuàng)建的 Renderscript 來(lái)創(chuàng)建一個(gè)自己需要的腳本( ScriptIntrinsic ),比如這里需要模糊,那就是 ScriptIntrinsicBlur ;
  3. 然后至少創(chuàng)建一個(gè) Allocation 類(lèi)來(lái)創(chuàng)建、分配內(nèi)存空間;
  4. 接著就是對(duì)圖像進(jìn)行一些處理,比如說(shuō)模糊處理;
  5. 處理完成后,需要?jiǎng)偛诺?Allocation 類(lèi)來(lái)填充分配好的內(nèi)存空間;
  6. 最后可以選擇性的對(duì)一些資源進(jìn)行回收。

文檔中的解釋永遠(yuǎn)很規(guī)矩,比較難懂,我們結(jié)合原博主 湫水長(zhǎng)天 的代碼來(lái)看一看步驟:

/**
 * @author Qiushui
 * @description 模糊圖片工具類(lèi)
 * @revision Xiarui 16.09.05
 */
public class BlurBitmapUtil {
  //圖片縮放比例
  private static final float BITMAP_SCALE = 0.4f;

  /**
   * 模糊圖片的具體方法
   *
   * @param context 上下文對(duì)象
   * @param image  需要模糊的圖片
   * @return 模糊處理后的圖片
   */
  public static Bitmap blurBitmap(Context context, Bitmap image,float blurRadius) {
    // 計(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并沒(méi)有使用VM來(lái)分配內(nèi)存,所以需要使用Allocation類(lèi)來(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(blurRadius);
    // 設(shè)置blurScript對(duì)象的輸入內(nèi)存
    blurScript.setInput(tmpIn);
    // 將輸出數(shù)據(jù)保存到輸出內(nèi)存中
    blurScript.forEach(tmpOut);

    // 將數(shù)據(jù)填充到Allocation中
    tmpOut.copyTo(outputBitmap);

    return outputBitmap;
  }
}

上面就是處理高斯模糊的代碼,其中注釋寫(xiě)的十分詳細(xì),而且已經(jīng)將圖片縮放處理了一下。結(jié)合剛才說(shuō)的步驟,大家應(yīng)該能有一個(gè)大概的印象,實(shí)在不懂也沒(méi)關(guān)系,這是一個(gè)工具類(lèi),直接 Copy 過(guò)來(lái)即可。

當(dāng)然,原博主將代碼封裝成輪子了,也可以直接在項(xiàng)目中引用 Gradle 也是可以的,但是我覺(jué)得源碼還是要看一看的。

簡(jiǎn)單的模糊

好了,有了一個(gè)大概的印象后,來(lái)看一下如何實(shí)現(xiàn)高斯模糊效果吧!

首先你可以在項(xiàng)目中直接引用原博主封裝的輪子:

compile 'com.qiushui:blurredview:0.8.1'

如果不想引用的話,就必須在當(dāng)前 Module 的 build.gradle 中添加如下代碼:

defaultConfig {
  renderscriptTargetApi 19
  renderscriptSupportModeEnabled true
}

等構(gòu)建好就可以使用了。如果構(gòu)建失敗的話,只需要把 minSdkVersion 設(shè)置成 19 就好了,暫時(shí)不知是何原因。不過(guò)從 StackOverflow 中了解到這是個(gè)Bug ,那就不必深究。

現(xiàn)在來(lái)看代碼實(shí)現(xiàn),首先布局文件中就一個(gè) ImageView ,沒(méi)啥好說(shuō)的,從上面的模糊圖片工具類(lèi)可以看出,要想獲得一個(gè)高斯模糊效果的圖片,需要三樣?xùn)|西:

Context:上下文對(duì)象
Bitmap:需要模糊的圖片
BlurRadius:模糊程度

這里需要注意一下:

目前這種方案只適用于 PNG 格式的圖片,而且圖片大小最好小一點(diǎn),雖然代碼中已經(jīng)縮放了圖片,但仍然可能會(huì)出現(xiàn)卡頓的情況。

現(xiàn)在只要設(shè)置一下圖片和模糊程度就好了:

/**
 * 初始化View
 */
@SuppressWarnings("deprecation")
private void initView() {
  basicImage = (ImageView) findViewById(R.id.iv_basic_pic);
  //拿到初始圖
  Bitmap initBitmap = BitmapUtil.drawableToBitmap(getResources().getDrawable(R.raw.pic));
  //處理得到模糊效果的圖
  Bitmap blurBitmap = BlurBitmapUtil.blurBitmap(this, initBitmap, 20f);
  basicImage.setImageBitmap(blurBitmap);
}

來(lái)看一下運(yùn)行圖:

可以看到,圖片已經(jīng)實(shí)現(xiàn)了模糊效果,而且速度還蠻快的,總的來(lái)說(shuō)通過(guò) BlurBitmapUtil.blurBitmap()就能得到一張模糊效果的圖 。

自定義模糊控件

原博主的輪子里給我們封裝了一個(gè)自定義的 BlurredView ,剛開(kāi)始我覺(jué)得沒(méi)必要自定義。后來(lái)發(fā)現(xiàn)自定義的原因是需要實(shí)現(xiàn)動(dòng)態(tài)模糊效果。

那為什么不能手動(dòng)去設(shè)置模糊程度呢?他給出的解釋是:

“如果使用上面的代碼進(jìn)行實(shí)時(shí)渲染的話,會(huì)造成界面嚴(yán)重的卡頓?!?/strong>

我也親自試了一試,確實(shí)有點(diǎn)卡。他實(shí)現(xiàn)動(dòng)態(tài)模糊處理的方案是這樣的:

“先將圖片進(jìn)行最大程度的模糊處理,再將原圖放置在模糊后的圖片上面,通過(guò)不斷改變?cè)瓐D的透明度(Alpha值)來(lái)實(shí)現(xiàn)動(dòng)態(tài)模糊效果?!?/strong>

這個(gè)方案確實(shí)很巧妙的實(shí)現(xiàn)動(dòng)態(tài)效果,但是注意如果要使用這種方式,就必須有兩張一模一樣的圖片。如果在代碼中直接寫(xiě),就需要兩個(gè)控件,如果圖片多的話,顯然是不可取的。所以輪子里有一個(gè)自定義的 BlurredView 。

不過(guò)這個(gè) BlurredView 封裝的不是太好,我刪減了一部分內(nèi)容,原因稍后再說(shuō)。先來(lái)看一下核心代碼。

首先是自定義的 BlurredView 繼承于 RelativeLayout ,在布局文件中可以看到,里面有兩個(gè) ImageView,且是疊在一起的。

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
       android:layout_width="match_parent"
       android:layout_height="match_parent">

  <ImageView
    android:id="@+id/blurredview_blurred_img"
    .../>

  <ImageView
    android:id="@+id/blurredview_origin_img"
    .../>

</FrameLayout>

同時(shí)也定義了一些屬性:

<resources>
  <declare-styleable name="BlurredView">
    <attr name="src" format="reference"/>
    <attr name="disableBlurred" format="boolean"/>
  </declare-styleable>
</resources>

一個(gè)是設(shè)置圖片,一個(gè)是設(shè)置是否禁用模糊。最后就是 BlurredView 類(lèi),代碼如下,有大量刪減,只貼出核心代碼:

/**
 * @author Qiushui
 * @description 自定義模糊View類(lèi)
 * @revision Xiarui 16.09.05
 */
public class BlurredView extends RelativeLayout {

  /*========== 全局相關(guān) ==========*/
  private Context mContext;//上下文對(duì)象
  private static final int ALPHA_MAX_VALUE = 255;//透明最大值
  private static final float BLUR_RADIUS = 25f;//最大模糊度(在0.0到25.0之間)

  /*========== 圖片相關(guān) ==========*/
  private ImageView mOriginImg;//原圖ImageView
  private ImageView mBlurredImg;//模糊后的ImageView
  private Bitmap mBlurredBitmap;//模糊后的Bitmap
  private Bitmap mOriginBitmap;//原圖Bitmap

  /*========== 屬性相關(guān) ==========*/
  private boolean isDisableBlurred;//是否禁用模糊效果

  ...

  /**
   * 以代碼的方式添加待模糊的圖片
   *
   * @param blurredBitmap 待模糊的圖片
   */
  public void setBlurredImg(Bitmap blurredBitmap) {
    if (null != blurredBitmap) {
      mOriginBitmap = blurredBitmap;
      mBlurredBitmap = BlurBitmapUtil.blurBitmap(mContext, blurredBitmap, BLUR_RADIUS);
      setImageView();
    }
  }
  ...

  /**
   * 填充ImageView
   */
  private void setImageView() {
    mBlurredImg.setImageBitmap(mBlurredBitmap);
    mOriginImg.setImageBitmap(mOriginBitmap);
  }

  /**
   * 設(shè)置模糊程度
   *
   * @param level 模糊程度, 數(shù)值在 0~100 之間.
   */
  @SuppressWarnings("deprecation")
  public void setBlurredLevel(int level) {
    //超過(guò)模糊級(jí)別范圍 直接拋異常
    if (level < 0 || level > 100) {
      throw new IllegalStateException("No validate level, the value must be 0~100");
    }

    //禁用模糊直接返回
    if (isDisableBlurred) {
      return;
    }

    //設(shè)置透明度
    mOriginImg.setAlpha((int) (ALPHA_MAX_VALUE - level * 2.55));
  }

  ...
}

從代碼中可以看到,最核心的就是下面三個(gè)方法:

setBlurredImg(Bitmap blurredBitmap):設(shè)置圖片,并復(fù)制兩份;
setImageView():給兩個(gè)ImageView設(shè)置相應(yīng)的圖片,內(nèi)部調(diào)用;
setBlurredLevel(int level):設(shè)置透明程度;

思路就是先選定一張圖片,一張作為原圖,一張作為模糊處理過(guò)的圖。再分別將這兩張圖設(shè)置給自定義 BlurredView 中的兩個(gè) ImageView ,最后處理模糊過(guò)后的那張圖的透明度。
好了,現(xiàn)在來(lái)寫(xiě)一個(gè)自定義的模糊效果圖,首先是布局,很簡(jiǎn)單:

 <com.blurdemo.view.BlurredView
    android:id="@+id/bv_custom_blur"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:src="@raw/pic"
    app:disableBlurred="false" />

可以看到,設(shè)置了圖片,設(shè)置了開(kāi)啟模糊,那么我們?cè)贏ctivity中只需設(shè)置透明程度即可:

private void initView() {
    customBView = (BlurredView) findViewById(R.id.bv_custom_blur);
    //設(shè)置模糊度
    customBView.setBlurredLevel(100);
  }

效果圖與上圖一樣,這里就不重復(fù)貼了??梢钥吹?,代碼簡(jiǎn)單了很多,不過(guò)僅僅因?yàn)榉奖愫?jiǎn)單可不是自定義 View 的作用,作用在于接下來(lái)要說(shuō)的 動(dòng)態(tài)模糊效果 的實(shí)現(xiàn)。

動(dòng)態(tài)模糊

我們先來(lái)看一下啥叫動(dòng)態(tài)模糊效果:

從圖中可以看到,隨著我們觸摸屏幕的時(shí)候,背景的模糊程度會(huì)跟著變化。如果要直接設(shè)置其模糊度會(huì)及其的卡頓,所以正如原博主所說(shuō),可以用兩張圖片來(lái)實(shí)現(xiàn)。

大體思路就是,上面的圖片模糊處理,下面的圖片不處理,然后通過(guò)手勢(shì)改變上面模糊圖片的透明度即可。

所以跟前面的代碼幾乎一樣,只需要重寫(xiě) onTouchEvent 方法即可:

/**
 * 初始化View
 */
private void initView() {
  customBView = (BlurredView) findViewById(R.id.bv_dynamic_blur);
  //設(shè)置初始模糊度
  initLevel = 100;
  customBView.setBlurredLevel(initLevel);
}

/**
 * 觸摸事件
 */
@Override
public boolean onTouchEvent(MotionEvent ev) {
  switch (ev.getAction()) {
    case MotionEvent.ACTION_DOWN:
      downY = ev.getY();
      break;

    case MotionEvent.ACTION_MOVE:
      float moveY = ev.getY();
      //手指滑動(dòng)距離
      float offsetY = moveY - downY;
      //屏幕高度 十倍是為了看出展示效果
      int screenY = getWindowManager().getDefaultDisplay().getHeight() * 10;
      //手指滑動(dòng)距離占屏幕的百分比
      movePercent = offsetY / screenY;
      currentLevel = initLevel + (int) (movePercent * 100);
      if (currentLevel < 0) {
        currentLevel = 0;
      }
      if (currentLevel > 100) {
        currentLevel = 100;
      }
      //設(shè)置模糊度
      customBView.setBlurredLevel(currentLevel);
      //更改初始模糊等級(jí)
      initLevel = currentLevel;
      break;
    case MotionEvent.ACTION_UP:
      break;
  }
  return super.onTouchEvent(ev);
}

從代碼中可以看到,這里是通過(guò)手指滑動(dòng)距離占屏幕的百分比來(lái)計(jì)算改變后的透明等級(jí)的,代碼應(yīng)該不難,很容易理解。當(dāng)然原博主博客中是通過(guò)進(jìn)度條來(lái)改變的,也是可以的,就不在贅述了。

與 RecylcerView 相結(jié)合

先來(lái)看一張效果圖,這個(gè)圖也是仿照原博主去實(shí)現(xiàn)的,但是還是有略微的不同。

本來(lái)的自定義 BlurredView 中還有幾段代碼是改變背景圖的位置的,因?yàn)橄M侠吕臅r(shí)候背景圖也是可以移動(dòng)的,但是從體驗(yàn)來(lái)看效果不是太好,上拉的過(guò)程中會(huì)出現(xiàn)留白的問(wèn)題。

雖然原博主給出了解決方案:手動(dòng)給背景圖增加一個(gè)高度,但這并不是最好的解決方式,所以我就此功能給刪去了,等找到更好的實(shí)現(xiàn)方式再來(lái)補(bǔ)充。

現(xiàn)在來(lái)看如何實(shí)現(xiàn)?首先布局就是底下一層自定義的 BlurredView ,上面一個(gè) RecylcerView,RecylcerView 有兩個(gè) Type ,一個(gè)是頭布局,一個(gè)是底下的列表,很簡(jiǎn)單,就不詳細(xì)說(shuō)了。

重點(diǎn)仍然是動(dòng)態(tài)模糊的實(shí)現(xiàn),在上面的動(dòng)態(tài)模糊中,我們采取了重寫(xiě) onTouchEvent 方法,但是這里剛好是 RecylcerView ,我們可以根據(jù)它的滾動(dòng)監(jiān)聽(tīng),也就是 onScrollListener 來(lái)完成動(dòng)態(tài)改變透明度,核心方法如下:

 //RecyclerView 滾動(dòng)監(jiān)聽(tīng)
  mainRView.setOnScrollListener(new RecyclerView.OnScrollListener() {
    @Override
    public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
      super.onScrollStateChanged(recyclerView, newState);
    }

    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
      super.onScrolled(recyclerView, dx, dy);
      //滾動(dòng)距離
      mScrollerY += dy;
      //根據(jù)滾動(dòng)距離控制模糊程度 滾動(dòng)距離是模糊程度的十倍
      if (Math.abs(mScrollerY) > 1000) {
        mAlpha = 100;
      } else {
        mAlpha = Math.abs(mScrollerY) / 10;
      }
      //設(shè)置透明度等級(jí)
      recyclerBView.setBlurredLevel(mAlpha);
    }
  });

代碼很簡(jiǎn)單,就是在 onScrolled 方法中計(jì)算并動(dòng)態(tài)改變透明度,只要掌握了原理,實(shí)現(xiàn)起來(lái)還是很容易的。

總結(jié)

從前面所有的動(dòng)態(tài)圖可以看到,運(yùn)行起來(lái)還是比較快的,但是我從 Android Monitor 中看到,在每一次剛開(kāi)始渲染模糊的時(shí)候,GPU 渲染的時(shí)間都很長(zhǎng),所以說(shuō)可能在性能方面還是有所欠佳。

當(dāng)然也可能跟模擬器有關(guān)系,真機(jī)上測(cè)試是很快的。而且貌似比 FastBlur 還快一點(diǎn),等有空測(cè)試幾個(gè)高斯模糊實(shí)現(xiàn)方法的性能,來(lái)對(duì)比一下。

到此,這種實(shí)現(xiàn)高斯模糊的方法已經(jīng)全部講完了,感謝原博主這么優(yōu)秀的文章,再次附上鏈接:

湫水長(zhǎng)天 – 教你一分鐘實(shí)現(xiàn)動(dòng)態(tài)模糊效果

其他參考資料

RenderScript – Android Developers

Android RenderScript入門(mén)(1)

高斯模糊效果實(shí)現(xiàn)方案及性能對(duì)比 – lcyFox

項(xiàng)目源碼

BlurDemo – IamXiaRui – Github

以上就是對(duì)Android 動(dòng)態(tài)高斯模糊效果教程的示例,謝謝大家對(duì)本站的支持!

相關(guān)文章

最新評(píng)論