編輯:關於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可以拖動
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(); } }
在Android中,數據的存儲分為兩種方式:1、直接以文件的形式存儲在目錄中2、以json格式存儲在數據庫中將數據以文件的存儲又分為兩種方式:1、生成.txt文件2、生成
這篇博客呢主要是寫gridview的多選以及單選的功能,並且獲取選中的值。首先呢,我做一下聲明,這個小程序呢是我借鑒某位大神的部分代碼,按照自己的需求重新編寫了一下。本來
前言 為了保證每周一篇的進度,又由於Vitamio新版本沒有發布, 決定推遲本地播放的一些功能(截圖、視頻時間、尺寸等),跳過直接寫在線播放部分的章節。從V
引言Android 中的Drawable是一個抽象的概念,換言之所有能被畫出來的都可以定義成Drawable(A Drawable is a general abstra