OpenGL
,其次是使用C++/C,使用Java代码是最慢的。但是Android推出RenderScript
之后,我们就有了新的选择,测试表明,使用RenderScript
的渲染效率和使用C/C++不相上下,但是使用RenderScript
却比使用JNI
简单地多!同时,Android团队提供了RenderScript
的支持库,使得在低版本的Android平台上也能使用。RenderScript
之前,对于模糊一张图片,需要注意的是,我们应该尽量不要使用原尺寸分辨率的图片,最好将图片缩小比例,这小渲染的效率要高一些。RenderScript
来模糊一张图片呢?废话不多说,先上核心代码:public class BlurBitmap { /*** 图片缩放比例*/ private static final float BITMAP_SCALE = 0.4f; /*** 最大模糊度(在0.0到25.0之间)*/ private static final float BLUR_RADIUS = 25f;/*** 模糊图片的具体方法** @param context 上下文对象* @param image需要模糊的图片* @return 模糊处理后的图片*/ public static Bitmap blur(Context context, Bitmap image) {// 计算图片缩小后的长宽int width = Math.round(image.getWidth() * BITMAP_SCALE);int height = Math.round(image.getHeight() * BITMAP_SCALE); // 将缩小后的图片做为预渲染的图片。Bitmap inputBitmap = Bitmap.createScaledBitmap(image, width, height, false);// 创建一张渲染后的输出图片。Bitmap outputBitmap = Bitmap.createBitmap(inputBitmap); // 创建RenderScript内核对象RenderScript rs = RenderScript.create(context);// 创建一个模糊效果的RenderScript的工具对象ScriptIntrinsicBlur blurScript = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs)); // 由于RenderScript并没有使用VM来分配内存,所以需要使用Allocation类来创建和分配内存空间。// 创建Allocation对象的时候其实内存是空的,需要使用copyTo()将数据填充进去。Allocation tmpIn = Allocation.createFromBitmap(rs, inputBitmap);Allocation tmpOut = Allocation.createFromBitmap(rs, outputBitmap); // 设置渲染的模糊程度, 25f是最大模糊度blurScript.setRadius(BLUR_RADIUS);// 设置blurScript对象的输入内存blurScript.setInput(tmpIn);// 将输出数据保存到输出内存中blurScript.forEach(tmpOut); // 将数据填充到Allocation中tmpOut.copyTo(outputBitmap); return outputBitmap; }}完成上面的代码后,需要在app的gradle文件中添加如下的支持:
defaultConfig { ...... renderscriptTargetApi 19 renderscriptSupportModeEnabled true}代码做了简单的注释以帮助理解,如果需要详细了解,可以查阅官方文档
将图片模糊后,接下来要考虑的是怎么实现动态模糊效,有一点需要注意的是,即使我们使用了RenderScript
这种高效的渲染方式,但是在实际测试中,渲染一张500*700分辨率的PNG格式图片,在我的Pro 6手机上,仍然需要50ms左右的时间,显然如果使用上面的代码进行实时渲染的话,会造成界面严重的卡顿。
既然实时渲染这条路走不通,那么就需要我们另辟蹊径了,我这里可以提供一种方法:先将图片进行最大程度的模糊处理,再将原图放置在模糊后的图片上面,通过不断改变原图的透明度(Alpha值)来实现动态模糊效果。
简单的代码如下:
public class MainActivity extends AppCompatActivity {/*** 原始图片控件*/ private ImageView mOriginImg;/*** 模糊后的图片控件*/ private ImageView mBluredImage;/*** 进度条SeekBar*/ private SeekBar mSeekBar;/*** 显示进度的文字*/ private TextView mProgressTv;/*** 透明度*/ private int mAlpha;/*** 原始图片*/ private Bitmap mTempBitmap;/*** 模糊后的图片*/ private Bitmap mFinalBitmap;@Override protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main); // 初始化视图initViews(); // 获取图片mTempBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.dayu);mFinalBitmap = BlurBitmap.blur(this, mTempBitmap); // 填充模糊后的图像和原图mBluredImage.setImageBitmap(mFinalBitmap);mOriginImg.setImageBitmap(mTempBitmap); // 处理seekbar滑动事件setSeekBar(); }/*** 初始化视图*/ private void initViews() {mBluredImage = (ImageView) findViewById(R.id.activity_main_blured_img);mOriginImg = (ImageView) findViewById(R.id.activity_main_origin_img);mSeekBar = (SeekBar) findViewById(R.id.activity_main_seekbar);mProgressTv = (TextView) findViewById(R.id.activity_main_progress_tv); }/*** 处理seekbar滑动事件*/ private void setSeekBar() {mSeekBar.setMax(100);mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {mAlpha = progress;mOriginImg.setAlpha((int) (255 - mAlpha * 2.55));mProgressTv.setText(String.valueOf(mAlpha)); }@Override public void onStartTrackingTouch(SeekBar seekBar) {}@Override public void onStopTrackingTouch(SeekBar seekBar) {}}); }}xml布局文件代码如下:
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent"><FrameLayoutandroid:layout_width="match_parent"android:layout_weight="1"android:layout_height="0dp"> <ImageView android:id="@+id/activity_main_blured_img" android:scaleType="centerCrop" android:src="@drawable/dayu" android:layout_width="match_parent" android:layout_height="match_parent"/> <ImageView android:id="@+id/activity_main_origin_img" android:scaleType="centerCrop" android:layout_width="match_parent" android:layout_height="match_parent"/> </FrameLayout><LinearLayoutandroid:orientation="vertical"android:layout_width="match_parent"android:layout_height="80dp"> <SeekBar android:layout_marginTop="@dimen/activity_vertical_margin" android:id="@+id/activity_main_seekbar" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="16dp" android:layout_marginRight="16dp"/> <TextView android:id="@+id/activity_main_progress_tv" android:text="0" android:textSize="24sp" android:layout_gravity="center" android:layout_width="wrap_content" android:layout_height="wrap_content"/> </LinearLayout> </LinearLayout>效果如下:
怎么样?是不是很简单的样子?只需要调用模糊处理方法,并在SeekBar的滑动监听里面调用原图像的setAlpha()
方法,来实现动态模糊效果。
你以为这样就完了?不不不,我们的目的并不是这么单纯,哦,不对,并不是这么简单。还记得文章开头的时候说了吗?我们的终极目的是要简单地模仿一下雅虎天气的界面效果。
仿雅虎天气界面
有了上面的基础,就可以很容易地模仿雅虎天气的界面效果。简单来说,在上面制作出的效果基础上,有以下两点需要注意的地方:
需要要监听滑动事件,然后再将背景图片调用setTop()
方法,将图片向上平移一段距离。
要向上平移图片,还需要手动增加图片的高度,不然图片向上平移后,底部就会有留白。设置图片高度的核心代码如下:
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);Display display = wm.getDefaultDisplay();Point point = new Point();display.getSize(point);// 获取到ImageView的高度int height = point.y;ViewGroup.LayoutParams params = imageView.getLayoutParams();params.width = ViewGroup.LayoutParams.MATCH_PARENT;// 将ImageView的高度增加100params.height = height + 100;// 应用更改设置imageView.requestLayout();完成上面两点的内容后,基本就可以模仿出雅虎天气的首页了。
总结
以上就是本文的全部内容了,实现后的效果是不是很赞呢?感兴趣的朋友快快自己动手操作起来吧,希望本文对大家开发Android能有所帮助。