Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android模糊示例-RenderScript-附效果圖與代碼

Android模糊示例-RenderScript-附效果圖與代碼

編輯:關於Android編程

 

1. 程序截圖

拖動紅色區域,可以顯示出清晰的汽車部分。拖動下面的滑塊,可以更改模糊程度。

\ \ \

2. 程序實現方法

實現思路,用FrameLayout搞了三層,最底下一層是清晰的圖片,中間一層是模糊的圖片,最上面的一層,是紅色區域,這一層是清晰的圖片。

 

	public static class PlaceholderFragment extends Fragment { // 新版android adt-bundle默認在activity中帶一個fragment,據說android stdio早就這樣了

		private ImageView mOriginIv;

		private ImageView mBlurIv;

		private ImageView mClearIv;

		private SeekBar mRadiusSb;

		public PlaceholderFragment() {
		}

		@Override
		public View onCreateView(LayoutInflater inflater, ViewGroup container,
				Bundle savedInstanceState) {
			View rootView = inflater.inflate(R.layout.fragment_main, container,
					false);
			return rootView;
		}

		@Override
		public void onActivityCreated(Bundle savedInstanceState) {
			super.onActivityCreated(savedInstanceState);
			mOriginIv = (ImageView) getActivity().findViewById(
					R.id.origin_image);
			mBlurIv = (ImageView) getActivity().findViewById(R.id.blur_image);
			mClearIv = (ImageView) getActivity().findViewById(R.id.clear_image);
			mRadiusSb = (SeekBar) getActivity().findViewById(
					R.id.radius_seekbar);
			drawBlurImage(); // 初始化模糊層。
			setSeekBarChangeListen(); // 設置SeekBar回調,滑塊位置變化的時候,更新模糊層。
                        // 延遲是為了保證view.getX,view.getWidth 這類方法能夠去到數值,這裡只是為了初始化,所以延遲執行比較好。 
                        // 如果要是每次可視化的時候,都要讀weidht和x,那麼可以再在Activity#onWindowFocusChange中調用。
			Runnable runnable = new Runnable() {

				@Override
				public void run() {
					OnMoveListener listener = new OnMoveListener() {

						@Override
						public void onMoved() {
							mOriginIv.buildDrawingCache();
							clear(mOriginIv.getDrawingCache(), mClearIv, 10); // 這是拿到View繪制圖像的好辦法
						}
					};
					MoveUtils.enableMove(mClearIv, listener);
				}
			};
			mClearIv.postDelayed(runnable, 500);
		}

		private void drawBlurImage() {
			mOriginIv.getViewTreeObserver().addOnPreDrawListener(
					new OnPreDrawListener() {

						@Override
						public boolean onPreDraw() {
							mOriginIv.getViewTreeObserver()
									.removeOnPreDrawListener(this);
							mOriginIv.buildDrawingCache();
							float radius = mRadiusSb.getProgress();
							if (radius < 0.1) { // RenderScript要求radius必須在0和25之間,不能等於
								radius = 0.1f;
							}
							if (radius > 24.9) {
								radius = 24.9f;
							}
							blur(mOriginIv.getDrawingCache(), mBlurIv, radius);
							clear(mOriginIv.getDrawingCache(), mClearIv, 10); // 這裡為了顯示邊框,偷懶了直接用了10px,實際上是5dip,在我的手機galaxy nexus上,1dip=2px,實際上應該換算一下的。
							return true; // 這個是參考文章中要求的,沒試過false。
						}
					});
		}

		private void setSeekBarChangeListen() {
			mRadiusSb.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {

				@Override
				public void onStopTrackingTouch(SeekBar arg0) {
				}

				@Override
				public void onStartTrackingTouch(SeekBar arg0) {
				}

				@Override
				public void onProgressChanged(SeekBar arg0, int arg1,
						boolean arg2) {
					drawBlurImage();
				}
			});
		}

		// 首先根據view的大小,從bkg生成一個剪裁後的圖像;然後根據radius,將剪裁後的圖像模糊處理;最後將模糊處理的圖像設置到view上。 
		@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
		private void blur(Bitmap bkg, View view, float radius) {
                       // 剪裁圖片的過程
			Bitmap overlay = Bitmap.createBitmap(
					(int) (view.getMeasuredWidth()),
					(int) (view.getMeasuredHeight()), Bitmap.Config.ARGB_8888);
			Canvas canvas = new Canvas(overlay);
			canvas.translate(-view.getX(), -view.getY()); // 這裡是設置坐標系原點
			canvas.drawBitmap(bkg, 0, 0, null); // // 這裡直接在新的坐標系的原點上繪制圖像,如果不設置坐標系的話,相當於在(view.getX(),view.getY)上繪制圖像,android向右是x軸正方形,向下時y軸正方向。
                        // 模糊圖片的過程
			RenderScript rs = RenderScript.create(getActivity()); // RenderScript要求apilevel 17,這個比較惡心,v8支持包也不是特別好用,真的要搞模糊的話,還是opencv jni來搞吧。
			Allocation overlayAlloc = Allocation.createFromBitmap(rs, overlay);
			ScriptIntrinsicBlur blur = ScriptIntrinsicBlur.create(rs,
					overlayAlloc.getElement());
			blur.setInput(overlayAlloc);
			blur.setRadius(radius);
			blur.forEach(overlayAlloc);
			overlayAlloc.copyTo(overlay);
                        // 設置圖片
			view.setBackground(new BitmapDrawable(getResources(), overlay));
			rs.destroy();
		}
                
                // 首先根據view的大小,從bkg生成一個剪裁後的圖像;然後將剪裁後的圖像設置到view上。
		private void clear(Bitmap bkg, ImageView view, int paddingPx) {
			Bitmap overlay = Bitmap.createBitmap(
					(int) (view.getMeasuredWidth() - paddingPx * 2),
					(int) (view.getMeasuredHeight() - paddingPx * 2),
					Bitmap.Config.ARGB_8888);
			Canvas canvas = new Canvas(overlay);
			canvas.translate(-view.getX() - paddingPx, -view.getY() - paddingPx);
			canvas.drawBitmap(bkg, 0, 0, null);
			view.setImageDrawable(new BitmapDrawable(getResources(), overlay));
		}

	}


 


3. 代碼下載

 

萬惡的CSDN上傳了代碼,好幾個小時了還沒審核完。。。注意代碼的minsdk我設置的比較高,是API Level17,沒辦法,RenderScript的支持庫沒搞定。

4. 幾個問題

RenderScript 雖然有support-v8支持庫,但是我搞了會,也沒編譯成功。也看到有帖子說在2.3.5上RenderScript有問題的。所以感覺不是特別靠譜,還是jni+opencv自己搞起來比較好,網上opencv相關的模糊算法很多。另外如果圖像很大,模糊處理比較耗時,最好是異步進行。

getWidth,getHeight,getLeft的調用時機 onStart、onReusme這些都不行,只能在onWindowFoucsChange。本文的示例是初始化的時候調用,所以可以延遲一會執行,如果要是每次從後台切換到前台,就要調用的話,那麼要在onWindowFoucsChange中調用。

使用canvas剪裁bitmap 注意坐標系,android向右是x軸正方形,向下時y軸正方向。

 

private void clear(Bitmap bkg, ImageView view, int paddingPx) {
			Bitmap overlay = Bitmap.createBitmap(
					(int) (view.getMeasuredWidth() - paddingPx * 2),
					(int) (view.getMeasuredHeight() - paddingPx * 2),
					Bitmap.Config.ARGB_8888);
			Canvas canvas = new Canvas(overlay);
			canvas.translate(-view.getX() - paddingPx, -view.getY() - paddingPx);
			canvas.drawBitmap(bkg, 0, 0, null);
			view.setImageDrawable(new BitmapDrawable(getResources(), overlay));
		}
獲取圖片繪制緩存

 

 

mOriginIv.buildDrawingCache();
clear(mOriginIv.getDrawingCache(), mClearIv, 10);
讓View可以拖動
寫了一個簡單的方法,通過View的Tag+onTouchEvent,實現View可以拖動。

 

 

package com.example.blurtest;

import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;

public class MoveUtils {
	private static final int STATE_IDLE = 0;
	private static final int STATE_MOVING = 1;
	private static final int MIN_GAP = 5;

	private static class Info {
		public int state = STATE_IDLE;
		public float lastX = -1;
		public float lastY = -1;
		public OnMoveListener listener;
	}

	private static Info getInfo(View view) {
		if (view.getTag() == null) {
			view.setTag(new Info());
		}
		return (Info) (view.getTag());
	}

	private static void tryToMove(View view, MotionEvent ev) {
		Info info = getInfo(view);
		if (info.state != STATE_MOVING) {
			return;
		}
		float x = ev.getX() - info.lastX;
		float y = ev.getY() - info.lastY;
		if (Math.abs(x) < MIN_GAP && Math.abs(y) < MIN_GAP) {
			return;
		}
		view.setX(view.getX() + x);
		view.setY(view.getY() + y);
		view.invalidate();
		info.listener.onMoved();
	}

	public static void enableMove(View target, OnMoveListener listener) {
		Info info = new Info();
		info.listener = listener;
		target.setTag(info);
		target.setOnTouchListener(new OnTouchListener() {

			@Override
			public boolean onTouch(View view, MotionEvent ev) {
				Info info = getInfo(view);
				int action = ev.getAction() & MotionEvent.ACTION_MASK;
				switch (action) {
				case MotionEvent.ACTION_DOWN:
					info.state = STATE_MOVING;
					info.lastX = ev.getX();
					info.lastY = ev.getY();
					view.setTag(info);
					break;
				case MotionEvent.ACTION_MOVE:
					tryToMove(view, ev);
					break;
				case MotionEvent.ACTION_UP:
					info.state = STATE_IDLE;
					info.lastX = ev.getX();
					info.lastY = ev.getY();
					view.setTag(info);
				default:
					break;
				}
				return true;
			}
		});
	}

	public static interface OnMoveListener {
		public void onMoved();
	}
}

 

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved