实现:
首先我做出来的自定义view是希望全局只要调一个方法就可以用的, 就像popupwindow那样, 所以我的思路是初始化整个布局, 然后以Toast的方式添加到屏幕最前端.
所以第一步: 初始化整个弹窗布局
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//弹出选项弹窗
hintPopupWindow.showPopupWindow(v);
}
});
ImageView imageView = (ImageView) findViewById(R.id.imageView);
imageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//弹出选项弹窗
hintPopupWindow.showPopupWindow(v);
}
});
//下面的操作是初始化弹出数据
ArrayList<String> strList = new ArrayList<>();
strList.add("选项item1选项item1选项item1选项item1");
strList.add("选项item2");
strList.add("选项item3");
ArrayList<View.OnClickListener> clickList = new ArrayList<>();
View.OnClickListener clickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this, "点击事件触发", Toast.LENGTH_SHORT).show();
}
};
clickList.add(clickListener);
clickList.add(clickListener);
clickList.add(clickListener);
clickList.add(clickListener);
//具体初始化逻辑看下面的图
hintPopupWindow = new HintPopupWindow(this, strList, clickList);
}
/**
* @param contentList 点击item内容的文字
* @param clickList 点击item的事件
*/
public void initLayout(List<String> contentList, List<View.OnClickListener> clickList){
//这是根布局
rootView = (ViewGroup) View.inflate(activity, R.layout.item_root_hintpopupwindow, null);
linearLayout = (ViewGroup) rootView.findViewById(R.id.linearLayout);
//格式化点击item, 将文字和click事件一一绑定上去
List<View> list = new ArrayList<>();
for(int x=0; x<contentList.size(); x++){
View view = View.inflate(activity, R.layout.item_hint_popupwindow, null);
TextView textView = (TextView) view.findViewById(R.id.tv_content);
View v_line = view.findViewById(R.id.v_line);
textView.setText(contentList.get(x));
linearLayout.addView(view);
list.add(view);
if(x == 0){
v_line.setVisibility(View.INVISIBLE);
}else{
v_line.setVisibility(View.VISIBLE);
}
}
for (int x=0; x<list.size(); x++){
list.get(x).setOnClickListener(clickList.get(x));
}
//这里给你根布局设置背景透明, 为的是让他看起来和activity的布局一样
params = new WindowManager.LayoutParams();
params.width = WindowManager.LayoutParams.MATCH_PARENT;
params.height = WindowManager.LayoutParams.MATCH_PARENT;
params.format = PixelFormat.RGBA_8888;//背景透明
params.gravity = Gravity.LEFT | Gravity.TOP;
//当点击根布局时, 隐藏
rootView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
gonePopupWindow();
}
});
rootView.setOnKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
//如果是显示状态那么隐藏视图
if(keyCode == KeyEvent.KEYCODE_BACK && isShow) gonePopupWindow();
return isShow;
}
});
}
第二步: 这里比较重要了, 当点击button, 弹出选项框的时候, 具体做哪些事情
/**
* 弹出选项弹窗
* @param locationView 默认在该view的下方弹出, 和popupWindow类似
*/
public void showPopupWindow(View locationView){
try {
//这个步骤是得到该view相对于屏幕的坐标, 注意不是相对于父布局哦!
int[] arr = new int[2];
locationView.getLocationOnScreen(arr);
linearLayout.measure(0, 0);
Rect frame = new Rect();
activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(frame);//得到状态栏高度
float x = arr[0] + locationView.getWidth() - linearLayout.getMeasuredWidth();
float y = arr[1] - frame.top + locationView.getHeight();
linearLayout.setX(x);
linearLayout.setY(y);
/*捕获当前activity的布局视图, 因为我们要动态模糊, 所以这个布局一定要是最新的,
*这样我们把模糊后的布局盖到屏幕上时, 才能让用户感觉不出来变化*/
View decorView = activity.getWindow().getDecorView().findViewById(android.R.id.content);
Bitmap bitmap = getBitmapByView(decorView);//这里是将view转成bitmap
setBlurBackground(bitmap);//这里是模糊图片, 这个是重点我会单独讲的, 因为效率很重要啊!!!
//这里就是使用WindowManager直接将我们处理好的view添加到屏幕最前端
windowManager = (WindowManager) activity.getSystemService(Context.WINDOW_SERVICE);
windowManager.addView(rootView, params);
//这一步就是有回弹效果的弹出动画, 我用属性动画写的, 很简单
showAnim(linearLayout, 0, 1, animDuration, true);
//视图被弹出来时得到焦点, 否则就捕获不到Touch事件
rootView.setFocusable(true);
rootView.setFocusableInTouchMode(true);
rootView.requestFocus();
rootView.requestFocusFromTouch();
}catch (Exception e){
e.printStackTrace();
}
}
到这里我们所有操作就都完成了 大家是不是感觉很简单, 嘿嘿嘿, 最难的坑其实是模糊图片那里, 因为我们是当用户点击弹出按钮的时候动态模糊的, 所以效率就很重要, 下面是我对activity视图bitmap的处理:
private void setBlurBackground(Bitmap bitmap) {
//将activity的bitmap缩小的9分之1,因为本身我就要对图片进行模糊处理,s所以不需要太高规格的图片
//将图片体积缩小,模糊效率也会等比例加快
Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap, bitmap.getWidth() / 3, bitmap.getHeight() / 3, false);
//将缩小后的图片模糊化
Bitmap blurBitmap = getBlurBitmap(activity, scaledBitmap, 5);
rootView.setAlpha(0);
rootView.setBackgroundDrawable(new BitmapDrawable(blurBitmap));
alphaAnim(rootView, 0, 1, animDuration);
}
当用户点下按钮时,我们需要立刻就将模糊后的图片显示出来, 下面是我的模糊图片代码:
/**
* android系统的模糊方法
* @param bitmap 要模糊的图片
* @param radius 模糊等级 >=0 && <=25
*/
public static Bitmap blurBitmap(Context context, Bitmap bitmap, int radius) {
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1){
//Let's create an empty bitmap with the same size of the bitmap we want to blur
Bitmap outBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);
//Instantiate a new Renderscript
RenderScript rs = RenderScript.create(context);
//Create an Intrinsic Blur Script using the Renderscript
ScriptIntrinsicBlur blurScript = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
//Create the Allocations (in/out) with the Renderscript and the in/out bitmaps
Allocation allIn = Allocation.createFromBitmap(rs, bitmap);
Allocation allOut = Allocation.createFromBitmap(rs, outBitmap);
//Set the radius of the blur
blurScript.setRadius(radius);
//Perform the Renderscript
blurScript.setInput(allIn);
blurScript.forEach(allOut);
//Copy the final bitmap created by the out Allocation to the outBitmap
allOut.copyTo(outBitmap);
//recycle the original bitmap
bitmap.recycle();
//After finishing everything, we destroy the Renderscript.
rs.destroy();
return outBitmap;
}else{
return bitmap;
}
}
android里面的高斯模糊我大概总结了一下 基本有三种, 优缺点都有, 我用的是系统推荐的, 速度比较快,而且也简单, 但只能支持android版本17以上, 但现在手机用android4.2以下的估计也很少了.
第二种就是利用glide自定义类继承BitmapTransformation来实现在加载图片时模糊图片,但和第一种差不多,也要android版本17以上才能用
第三种就是用java层的代码, 手动算出像素值, 因为图片处理的代码逻辑都是用java实现的, 所以效率极差, 不推荐.
最后在说一下那个弹出蠕动的动画, 很简单20行代码就ok了, 我是用属性动画写的, 让弹窗view的宽和高的规模从0到1, 然后在从1到0.95, 这样就造成了一个弹出的动态效果, 很easy吧
private void showAnim(final View view, float start, final float end, int duration, final boolean isWhile) {
ValueAnimator va = ValueAnimator.ofFloat(start, end).setDuration(duration);
va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float value = (float) animation.getAnimatedValue();
view.setPivotX(view.getWidth());
view.setPivotY(0);
view.setScaleX(value);
view.setScaleY(value);
}
});
va.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
if (isWhile) showAnim(view, end, 0.95f, animDuration / 3, false);
}
});
va.start();
}
demo下载http://download.youkuaiyun.com/detail/qq_35549248/9847375