当前位置: 代码迷 >> Android >> Android模糊不清示例-RenderScript-附效果图与代码
  详细解决方案

Android模糊不清示例-RenderScript-附效果图与代码

热度:75   发布时间:2016-04-28 05:13:09.0
Android模糊示例-RenderScript-附效果图与代码

本文链接    http://blog.csdn.net/xiaodongrush/article/details/31031411

参考链接    Android高级模糊技术    http://stackoverflow.com/questions/14879439/renderscript-via-the-support-library

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上传了代码,好几个小时了还没审核完。。。http://download.csdn.net/detail/u011267546/7502603     注意代码的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();	}}

    

  相关解决方案