標籤:resource androi enter 緩衝 super 分享 後台 efault article
本文連結 http://blog.csdn.net/xiaodongrush/article/details/31031411
參考連結 Android進階模糊技術 http://stackoverflow.com/questions/14879439/renderscript-via-the-support-library
1. 程式
拖動紅色地區,能夠顯示出清晰的汽車部分。
拖動以下的滑塊,能夠更改模糊程度。
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveGlhb2RvbmdydXNo/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" >
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() {}@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {View rootView = inflater.inflate(R.layout.fragment_main, container,false);return rootView;}@Overridepublic 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() {@Overridepublic void run() {OnMoveListener listener = new OnMoveListener() {@Overridepublic 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() {@Overridepublic 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。在My Phonegalaxy nexus上。1dip=2px,實際上應該換算一下的。return true; // 這個是參考文章中要求的。沒試過false。}});}private void setSeekBarChangeListen() {mRadiusSb.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {@Overridepublic void onStopTrackingTouch(SeekBar arg0) {}@Overridepublic void onStartTrackingTouch(SeekBar arg0) {}@Overridepublic 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() {@Overridepublic 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();}}
Android模糊示範範例-RenderScript-附與代碼