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