微博往往是各个PM&设计在出需求和设计稿争相模仿的对象,在微博中大家点的最多应该就是中间的小+号。点击加号弹出一个页面,背面是当前页面高斯模糊后的效果,各个按钮在上弹是呈现果冻效果。在实现上我们需要的关注的技术点有:
- 如何取到当前页面的图片
- 如果对图片进行高斯模糊处理
- 如果处理动画让弹出不是很突兀
1. 获取当前页面的截图
获取当前页面的截图我们可以通过以下代码:
/**
* 得到当前屏幕的bitmap
*
* @param rootView
* @return
*/
public static Bitmap getScreenBitmap(Activity activity, View rootView) {
View viewScreen = rootView;
int windowWidth = activity.getWindow().getWindowManager().getDefaultDisplay().getWidth();
int windowHeight = activity.getWindow().getWindowManager().getDefaultDisplay().getHeight();
// 获取状态栏高度
Rect rect = new Rect();
viewScreen.getWindowVisibleDisplayFrame(rect);
int statusBarHeights = rect.top;
viewScreen.setDrawingCacheEnabled(true);
viewScreen.buildDrawingCache();
if (windowHeight > rect.height() + rect.top) {
windowHeight = rect.height() + rect.top;
}
Bitmap bitmap = Bitmap.createBitmap(viewScreen.getDrawingCache(), 0, statusBarHeights, windowWidth, windowHeight - statusBarHeights);
viewScreen.destroyDrawingCache();
return bitmap;
}```
在这里,我们要注意的是如果直接将上面的bitmap去进行高斯模糊是非常耗时的,所以,所以我们一定要经过处理。因为我们之后要将图片进行高斯模糊,所以我们需要的bitmap不需要特别的清楚,进行如下处理:
Matrix matrix = new Matrix();
matrix.setScale(0.3f, 0.3f);
if (windowHeight > rect.height() + rect.top) {
windowHeight = rect.height() + rect.top;
}
Bitmap bitmap = Bitmap.createBitmap(viewScreen.getDrawingCache(), 0, statusBarHeights, windowWidth, windowHeight - statusBarHeights, matrix, false);
viewScreen.destroyDrawingCache();```
2.高斯模糊
如果api大于Honeycomb的话,我们可以使用api提供的RenderScript来进行高斯模糊处理,效率非常的高。代码如下:
Bitmap bitmap = sentBitmap.copy(sentBitmap.getConfig(), true);
final RenderScript rs = RenderScript.create(context);
final Allocation input = Allocation.createFromBitmap(rs,
sentBitmap, Allocation.MipmapControl.MIPMAP_NONE,
Allocation.USAGE_SCRIPT);
final Allocation output = Allocation.createTyped(rs,
input.getType());
final ScriptIntrinsicBlur script = ScriptIntrinsicBlur.create(rs,
Element.U8_4(rs));
script.setRadius(radius /* e.g. 3.f */);
script.setInput(input);
script.forEach(output);
output.copyTo(bitmap);
但是对于API版本低于Honeycomb的设备怎么处理呢,我们这里可以通过JAVA来进行高斯模糊处理代码如下:
/**
* 高斯模糊,当版本低于17时使用java方法来进行模糊
*
* @param sentBitmap 需要模糊的bitmap
* @param radius 模糊半径
* @return 模糊后的bitmap对象
*/
private static Bitmap stackblur(Bitmap sentBitmap,
int radius) {
Bitmap bitmap = null;
try {
bitmap = sentBitmap.copy(sentBitmap.getConfig(), true);
} catch (OutOfMemoryError e) {
e.printStackTrace();
return sentBitmap;
}
if (radius < 1) {
return (null);
}
int w = bitmap.getWidth();
int h = bitmap.getHeight();
int[] pix = new int[w * h];
bitmap.getPixels(pix, 0, w, 0, 0, w, h);
int wm = w - 1;
int hm = h - 1;
int wh = w * h;
int div = radius + radius + 1;
int r[] = new int[wh];
int g[] = new int[wh];
int b[] = new int[wh];
int rsum, gsum, bsum, x, y, i, p, yp, yi, yw;
int vmin[] = new int[Math.max(w, h)];
int divsum = (div + 1) >> 1;
divsum *= divsum;
int dv[] = new int[256 * divsum];
for (i = 0; i < 256 * divsum; i++) {
dv[i] = (i / divsum);
}
yw = yi = 0;
int[][] stack = new int[div][3];
int stackpointer;
int stackstart;
int[] sir;
int rbs;
int r1 = radius + 1;
int routsum, goutsum, boutsum;
int rinsum, ginsum, binsum;
for (y = 0; y < h; y++) {
rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
for (i = -radius; i <= radius; i++) {
p = pix[yi + Math.min(wm, Math.max(i, 0))];
sir = stack[i + radius];
sir[0] = (p & 0xff0000) >> 16;
sir[1] = (p & 0x00ff00) >> 8;
sir[2] = (p & 0x0000ff);
rbs = r1 - Math.abs(i);
rsum += sir[0] * rbs;
gsum += sir[1] * rbs;
bsum += sir[2] * rbs;
if (i > 0) {
rinsum += sir[0];
ginsum += sir[1];
binsum += sir[2];
} else {
routsum += sir[0];
goutsum += sir[1];
boutsum += sir[2];
}
}
stackpointer = radius;
for (x = 0; x < w; x++) {
r[yi] = dv[rsum];
g[yi] = dv[gsum];
b[yi] = dv[bsum];
rsum -= routsum;
gsum -= goutsum;
bsum -= boutsum;
stackstart = stackpointer - radius + div;
sir = stack[stackstart % div];
routsum -= sir[0];
goutsum -= sir[1];
boutsum -= sir[2];
if (y == 0) {
vmin[x] = Math.min(x + radius + 1, wm);
}
p = pix[yw + vmin[x]];
sir[0] = (p & 0xff0000) >> 16;
sir[1] = (p & 0x00ff00) >> 8;
sir[2] = (p & 0x0000ff);
rinsum += sir[0];
ginsum += sir[1];
binsum += sir[2];
rsum += rinsum;
gsum += ginsum;
bsum += binsum;
stackpointer = (stackpointer + 1) % div;
sir = stack[(stackpointer) % div];
routsum += sir[0];
goutsum += sir[1];
boutsum += sir[2];
rinsum -= sir[0];
ginsum -= sir[1];
binsum -= sir[2];
yi++;
}
yw += w;
}
for (x = 0; x < w; x++) {
rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
yp = -radius * w;
for (i = -radius; i <= radius; i++) {
yi = Math.max(0, yp) + x;
sir = stack[i + radius];
sir[0] = r[yi];
sir[1] = g[yi];
sir[2] = b[yi];
rbs = r1 - Math.abs(i);
rsum += r[yi] * rbs;
gsum += g[yi] * rbs;
bsum += b[yi] * rbs;
if (i > 0) {
rinsum += sir[0];
ginsum += sir[1];
binsum += sir[2];
} else {
routsum += sir[0];
goutsum += sir[1];
boutsum += sir[2];
}
if (i < hm) {
yp += w;
}
}
yi = x;
stackpointer = radius;
for (y = 0; y < h; y++) {
// Preserve alpha channel: ( 0xff000000 & pix[yi] )
pix[yi] = (0xff000000 & pix[yi]) | (dv[rsum] << 16)
| (dv[gsum] << 8) | dv[bsum];
rsum -= routsum;
gsum -= goutsum;
bsum -= boutsum;
stackstart = stackpointer - radius + div;
sir = stack[stackstart % div];
routsum -= sir[0];
goutsum -= sir[1];
boutsum -= sir[2];
if (x == 0) {
vmin[y] = Math.min(y + r1, hm) * w;
}
p = x + vmin[y];
sir[0] = r[p];
sir[1] = g[p];
sir[2] = b[p];
rinsum += sir[0];
ginsum += sir[1];
binsum += sir[2];
rsum += rinsum;
gsum += ginsum;
bsum += binsum;
stackpointer = (stackpointer + 1) % div;
sir = stack[stackpointer];
routsum += sir[0];
goutsum += sir[1];
boutsum += sir[2];
rinsum -= sir[0];
ginsum -= sir[1];
binsum -= sir[2];
yi += w;
}
}
bitmap.setPixels(pix, 0, w, 0, 0, w, h);
return (bitmap);
}
此外,如果我们对性能要求比较高的话,我们还可以通过jni调用的方式来使用C++处理我们的bitmap,效率会相对于java代码处理有很大的提升,尽管如此依然没有RenderScript处理来得效率高。jni处理高斯模糊如下:
extern "C"
JNIEXPORT
jintArray JNICALL
Java_com_focustech_android_jni_SFJniUtils_stackBlur(
JNIEnv *env, jobject obj, jintArray array1, jint w, jint h, jint radius) {
const jsize length = env->GetArrayLength(array1);
jintArray newArray = env->NewIntArray(length);
jint *pix;
pix = env->GetIntArrayElements(array1, NULL);
jint *narr = env->GetIntArrayElements(newArray, NULL);
jint wm = w - 1;
jint hm = h - 1;
jint wh = w * h;
jint div = radius + radius + 1;
jint *r = (jint *) malloc(wh * sizeof(jint));
jint *g = (jint *) malloc(wh * sizeof(jint));
jint *b = (jint *) malloc(wh * sizeof(jint));
jint rsum, gsum, bsum, x, y, i, p, yp, yi, yw;
jint *vmin = (jint *) malloc(MAX(w, h) * sizeof(jint));
jint divsum = (div + 1) >> 1;
divsum *= divsum;
jint *dv = (jint *) malloc(256 * divsum * sizeof(jint));
for (i = 0; i < 256 * divsum; i++) {
dv[i] = (i / divsum);
}
yw = yi = 0;
jint(*stack)[3] = (jint(*)[3]) malloc(div * 3 * sizeof(jint));
jint stackpointer;
jint stackstart;
jint *sir;
jint rbs;
jint r1 = radius + 1;
jint routsum, goutsum, boutsum;
jint rinsum, ginsum, binsum;
for (y = 0; y < h; y++) {
rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
for (i = -radius; i <= radius; i++) {
p = pix[yi + (MIN(wm, MAX(i, 0)))];
sir = stack[i + radius];
sir[0] = (p & 0xff0000) >> 16;
sir[1] = (p & 0x00ff00) >> 8;
sir[2] = (p & 0x0000ff);
rbs = r1 - ABS(i);
rsum += sir[0] * rbs;
gsum += sir[1] * rbs;
bsum += sir[2] * rbs;
if (i > 0) {
rinsum += sir[0];
ginsum += sir[1];
binsum += sir[2];
}
else {
routsum += sir[0];
goutsum += sir[1];
boutsum += sir[2];
}
}
stackpointer = radius;
for (x = 0; x < w; x++) {
r[yi] = dv[rsum];
g[yi] = dv[gsum];
b[yi] = dv[bsum];
rsum -= routsum;
gsum -= goutsum;
bsum -= boutsum;
stackstart = stackpointer - radius + div;
sir = stack[stackstart % div];
routsum -= sir[0];
goutsum -= sir[1];
boutsum -= sir[2];
if (y == 0) {
vmin[x] = MIN(x + radius + 1, wm);
}
p = pix[yw + vmin[x]];
sir[0] = (p & 0xff0000) >> 16;
sir[1] = (p & 0x00ff00) >> 8;
sir[2] = (p & 0x0000ff);
rinsum += sir[0];
ginsum += sir[1];
binsum += sir[2];
rsum += rinsum;
gsum += ginsum;
bsum += binsum;
stackpointer = (stackpointer + 1) % div;
sir = stack[(stackpointer) % div];
routsum += sir[0];
goutsum += sir[1];
boutsum += sir[2];
rinsum -= sir[0];
ginsum -= sir[1];
binsum -= sir[2];
yi++;
}
yw += w;
}
for (x = 0; x < w; x++) {
rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
yp = -radius * w;
for (i = -radius; i <= radius; i++) {
yi = MAX(0, yp) + x;
sir = stack[i + radius];
sir[0] = r[yi];
sir[1] = g[yi];
sir[2] = b[yi];
rbs = r1 - ABS(i);
rsum += r[yi] * rbs;
gsum += g[yi] * rbs;
bsum += b[yi] * rbs;
if (i > 0) {
rinsum += sir[0];
ginsum += sir[1];
binsum += sir[2];
}
else {
routsum += sir[0];
goutsum += sir[1];
boutsum += sir[2];
}
if (i < hm) {
yp += w;
}
}
yi = x;
stackpointer = radius;
for (y = 0; y < h; y++) {
// Preserve alpha channel: ( 0xff000000 & pix[yi] )
pix[yi] = (0xff000000 & pix[yi]) | (dv[rsum] << 16) | (dv[gsum] << 8) | dv[bsum];
rsum -= routsum;
gsum -= goutsum;
bsum -= boutsum;
stackstart = stackpointer - radius + div;
sir = stack[stackstart % div];
routsum -= sir[0];
goutsum -= sir[1];
boutsum -= sir[2];
if (x == 0) {
vmin[y] = MIN(y + r1, hm) * w;
}
p = x + vmin[y];
sir[0] = r[p];
sir[1] = g[p];
sir[2] = b[p];
rinsum += sir[0];
ginsum += sir[1];
binsum += sir[2];
rsum += rinsum;
gsum += ginsum;
bsum += binsum;
stackpointer = (stackpointer + 1) % div;
sir = stack[stackpointer];
routsum += sir[0];
goutsum += sir[1];
boutsum += sir[2];
rinsum -= sir[0];
ginsum -= sir[1];
binsum -= sir[2];
yi += w;
}
}
free(r);
free(g);
free(b);
free(vmin);
free(dv);
free(stack);
for (jint x = 0; x < length; x++) {
narr[x] = pix[x];
}
env->ReleaseIntArrayElements(newArray, narr, NULL);
env->ReleaseIntArrayElements(array1, pix, NULL);
return newArray;
}
3. 处理动画效果
这点是我在实现需求的时候关注到的一点,如果不加一些中间动画,整个效果会显得很突兀。细节是决定一个产品的好于坏的关键,尽量做到完美。
贴出效果图:
动画相关代码:
//开始动画
private void setAni(View iv, int delay) {
ObjectAnimator voiceReplyOa = ObjectAnimator.ofFloat(iv, View.TRANSLATION_Y, 600, 0).setDuration(350);
voiceReplyOa.setInterpolator(new BounceOutInterpolator());
voiceReplyOa.setStartDelay(delay);
voiceReplyOa.start();
AnimatorSet as = new AnimatorSet();
as.playTogether(
ObjectAnimator.ofFloat(mWhiteMaskIv, View.ALPHA, 0f, 1f),
ObjectAnimator.ofFloat(mBlurIv, View.ALPHA, 0f, 1f),
ObjectAnimator.ofFloat(mRepWayLl, View.ALPHA, 0f, 1f)
);
as.setDuration(150);
as.setInterpolator(new AccelerateInterpolator());
as.start();
}
/**
* <加速--稍微突出目标值然后恢复到默认值>
*
* @author liuzaibing
* @version [版本号, 2016/11/11]
* @see [相关类/方法]
* @since [V1]
*/
public class BounceOutInterpolator implements Interpolator {
public BounceOutInterpolator() {
}
@Override
public float getInterpolation(float input) {
return ((input = input - 1) * input * ((1.70158f + 1) * input + 1.70158f) + 1);
}
}
弹窗是用Popupwidow来实现的,所有的就到这里,如果有疑问,可以邮件。