(原创)关于PopupWindow的showAsDropDown()和showAtLocation()使用方式及其区别
先看下demo的效果图,说明都在代码里面注释。
以下是个人编写代码:
1.MainActivity部分
package com.example.lainanzhou.popupwindoewlocation;
import android.app.Activity;
import android.graphics.drawable.BitmapDrawable;
import android.os.Bundle;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.Interpolator;
import android.view.animation.ScaleAnimation;
import android.widget.Button;
import android.widget.PopupWindow;
import android.widget.RelativeLayout;
import android.widget.TextView;
/**
* Android的对话框有两种:PopupWindow和AlertDialog。它们的不同点在于:
* •AlertDialog的位置固定,而PopupWindow的位置可以随意
* •AlertDialog是非阻塞线程的,而PopupWindow是阻塞线程的
* •PopupWindow的位置按照有无偏移分,可以分为偏移和无偏移两种;按照参照物的不同,
* 可以分为相对于某个控件(Anchor锚)和相对于父控件。具体如下
* •showAsDropDown(View anchor):相对某个控件的位置(正左下方),无偏移
* •showAsDropDown(View anchor,int xoff,int yoff):相对某个控件的位置,有偏移
* •showAtLocation(View parent,int gravity,int x,int y):相对于父控件的位置
* (例如正中央Gravity.CENTER,下方Gravity.BOTTOM等),可以设置偏移或无偏移
*/
public class MainActivity extends Activity implements View.OnClickListener {
private TextView mTv;
private TextView mMainView;
private RelativeLayout mRl_parent;
private PopupWindow mPopupWindow;
private int mWidth;
private boolean mIsClick5;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTv = (TextView) findViewById(R.id.tv);
mMainView = (TextView) findViewById(R.id.tv_mainView);
mRl_parent = (RelativeLayout) findViewById(R.id.rl_parent);
Button mButton01 = (Button) findViewById(R.id.button01);
Button mButton02 = (Button) findViewById(R.id.button02);
Button mButton03 = (Button) findViewById(R.id.button03);
Button mButton04 = (Button) findViewById(R.id.button04);
Button mButton05 = (Button) findViewById(R.id.button05);
mButton01.setOnClickListener(this);
mButton02.setOnClickListener(this);
mButton03.setOnClickListener(this);
mButton04.setOnClickListener(this);
mButton05.setOnClickListener(this);
}
/**
* 根据类型设置显示的popupWindow方式
*
* @param type 1.为直接显示在某控件下方+点击外部不可关闭
* 2.显示在某控件下方+点击外部可关闭
* 3.相对父容器中心显示位置+点击外部可关闭
* 4.相对父容器脚部显示位置+点击外部可关闭
* 5.默认点击外部可关闭
*/
private void initPopupWindow(int type) {
LayoutInflater layoutInflater = LayoutInflater.from(this);
View popupWindow = layoutInflater.inflate(R.layout.popup_window, null);
//mode共有三种情况,取值分别为MeasureSpec.UNSPECIFIED, MeasureSpec.EXACTLY, MeasureSpec.AT_MOST。
// MeasureSpec.EXACTLY是(完全)精确尺寸,父元素决定自元素的确切大小,子元素将被限定在给定的边界里而忽略它本身大小;
// 当我们将控件的layout_width或layout_height指定为具体数值时
// 如andorid:layout_width="50dip",或者为FILL_PARENT是,都是控件大小已经确定的情况,都是精确尺寸。
// MeasureSpec.AT_MOST是(至多)最大尺寸,当控件的layout_width或layout_height指定为WRAP_CONTENT时,
// 控件大小一般随着控件的子空间或内容进行变化,此时控件尺寸只要不超过父控件允许的最大尺寸即可。
// 因此,此时的mode是AT_MOST,size给出了父控件允许的最大尺寸。
// MeasureSpec.UNSPECIFIED是未指定尺寸,父元素不对子元素施加任何束缚,子元素可以得到任意想要的大小;
// 这种情况不多,一般都是父控件是AdapterView,通过measure方法传入的模式。
//以下方式是为了在popupWindow还没有弹出显示之前就测量获取其宽高(单位是px相熟)
int w = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
int h = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
TextView mTextView = (TextView) popupWindow.findViewById(R.id.tv_popupTv);
mTextView.measure(w, h);
mWidth = mTextView.getMeasuredWidth();//获取测量宽度px
int mHeight = mTextView.getMeasuredHeight();//获取测量高度px
//设置点击popupWindow里面文本可以dismiss该popupWindow
popupWindow.findViewById(R.id.tv_popupTv).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mPopupWindow != null && mPopupWindow.isShowing()) {
mPopupWindow.dismiss();
}
}
});
// 创建一个PopupWindow
// 参数1:contentView 指定PopupWindow的显示View
// 参数2:width 指定PopupWindow的width可以固定死某些数值:
// 如果不想固定死可以设置为ViewGroup.LayoutParams.WRAP_CONTENT/MATCH_CONTENT
// 参数3:height 指定PopupWindow的height
mPopupWindow = new PopupWindow(popupWindow, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
//设置动画两种方式:动画效果可以参考该网址 http://blog.youkuaiyun.com/xiaanming/article/details/8997260
//方式1:xml配置文件
// mPopupWindow.setAnimationStyle(R.anim.popupwindow_enter);
//方式2:直接设置该popupWindow中的View的动画
// setPopupAnimation(popupWindow);
mPopupWindow.setFocusable(true); //这里很重要,设置该popupWindow可以获取焦点,不然无法响应点击事件
switch (type) {
case 1:
//方式2:直接设置该popupWindow中的View的动画
setPopupAnimation(popupWindow);
//6.0无效
mPopupWindow.setOutsideTouchable(false);//设置点击外面不可以消失~注意该效果在设置背景的情况下是无效的
break;
case 2:
//方式1:xml配置文件
mPopupWindow.setAnimationStyle(R.anim.popupwindow_enter);
mPopupWindow.setBackgroundDrawable(new BitmapDrawable());
mPopupWindow.setOutsideTouchable(true);//设置点击外面可以消失~注意则必须要设置该popupWindow背景才有效
break;
case 3:
//方式2:直接设置该popupWindow中的View的动画
setPopupAnimation(popupWindow);
//6.0无效
mPopupWindow.setOutsideTouchable(false);//设置点击外面不可以消失~注意该效果在设置背景的情况下是无效的
break;
case 4:
//方式1:xml配置文件
mPopupWindow.setAnimationStyle(R.anim.popupwindow_enter);
mPopupWindow.setBackgroundDrawable(new BitmapDrawable());
mPopupWindow.setOutsideTouchable(true);//设置点击外面可以消失~注意则必须要设置该popupWindow背景才有效
break;
case 5:
mMainView.setVisibility(mIsClick5 ? View.VISIBLE : View.GONE);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, dip2px(48));
mMainView.measure(w, h);
int mMainViewWidth = mMainView.getMeasuredWidth();//获取测量宽度px
int width = (mTv.getWidth() - mMainViewWidth) / 2;//获取x轴偏移量px
params.setMargins(mTv.getLeft() + width, mTv.getHeight(), 0, 0);
mMainView.setLayoutParams(params);//设置位置
if (mIsClick5)
mMainView.setAnimation(getAnimation());//设置动画
break;
default:
//方式1:xml配置文件
mPopupWindow.setAnimationStyle(R.anim.popupwindow_enter);
mPopupWindow.setBackgroundDrawable(new BitmapDrawable());
mPopupWindow.setOutsideTouchable(true);//设置点击外面可以消失~注意则必须要设置该popupWindow背景才有效
break;
}
}
/**
* 动画效果
*
* @return
*/
public Animation getAnimation() {
AlphaAnimation localAlphaAnimation = new AlphaAnimation(0.0F, 1.0F);
localAlphaAnimation.setInterpolator(new Interpolator() {
public float getInterpolation(float paramFloat) {
return 10.0F * paramFloat;
}
});
localAlphaAnimation.setDuration(500L);
ScaleAnimation localScaleAnimation = new ScaleAnimation(0.0F, 1.0F, 0.0F, 1.0F, 1, 0.5F, -1, 0.0F);
localScaleAnimation.setDuration(500L);
AnimationSet localAnimationSet = new AnimationSet(true);
localAnimationSet.addAnimation(localScaleAnimation);
localAnimationSet.addAnimation(localAlphaAnimation);
return localAnimationSet;
}
/**
* 设置组合动画
*
* @param paramView
*/
private void setPopupAnimation(View paramView) {
//透明度动画
AlphaAnimation localAlphaAnimation = new AlphaAnimation(0.0F, 1.0F);
localAlphaAnimation.setInterpolator(new Interpolator() {
public float getInterpolation(float paramFloat) {
return 10.0F * paramFloat;
}
});
localAlphaAnimation.setDuration(800L);//动画持续时长
//缩放动画:
// 参数:
// 1.为x轴起始缩放度 2.为x结束缩放度
// 3.为y起始缩放度 4.为y结束缩放度
// 5.为相对x轴类型为顶部 6.为该类型上起始度(0.5f为中间位置)
// 7.为相对y轴类型 8.为该类型起始位置(0F为原位置)
ScaleAnimation localScaleAnimation = new ScaleAnimation(0F, 1.0F, 0F, 1.0F, Animation.ZORDER_TOP, 0.5F, Animation.ZORDER_TOP, 0F);
localScaleAnimation.setDuration(500L);//动画持续时长
AnimationSet localAnimationSet = new AnimationSet(true);
localAnimationSet.addAnimation(localScaleAnimation);
localAnimationSet.addAnimation(localAlphaAnimation);
paramView.startAnimation(localAnimationSet);
}
/**
* dip与px之间转换
*
* @param dipValue
* @return
*/
private int dip2px(float dipValue) {
final float scale = getResources().getDisplayMetrics().density;
return (int) (dipValue * scale + 0.5f);
}
private int px2dip(float pxValue) {
final float scale = getResources().getDisplayMetrics().density;
return (int) (pxValue / scale + 0.5f);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
// 相对某个控件的位置(正左下方),无偏移
case R.id.button01:
initPopupWindow(1);
mPopupWindow.showAsDropDown(mTv);
break;
// 相对某个控件的位置(正左下方),有偏移
case R.id.button02:
initPopupWindow(2);
//以下为分步介绍控件获取中间位置偏移量方式:(对应控件宽度-popup宽度)/2
int tv_width = mTv.getWidth();//获取对应的控件view宽度px值
int popup_width = dip2px(120);//将popupWindow宽度转为px值(这里的popup宽度是写死了的)
int width = (tv_width - mWidth) / 2;//获取x轴偏移量px
mPopupWindow.showAsDropDown(mTv, width, 0);//设置x轴偏移量:注意单位为px
break;
// 相对于父控件的位置,无偏移~参数1为父容器~参数2为相对父容器相对类型~参数34为偏移量
case R.id.button03:
initPopupWindow(3);
//int[] locaitons = new int[2];//存放相应控件在屏幕的xy轴坐标点;单位px
//mTv.getLocationOnScreen(locaitons);//locaitons[0]为x轴 locaitons[1]为y轴
// X、Y方向偏移量:设置x轴偏移量为相应控件中心;y轴无偏移
mPopupWindow.showAtLocation(mRl_parent, Gravity.CENTER, 0, 0);
break;
// 相对于父控件的位置,有偏移~参数1为父容器~参数2为相对父容器相对类型~参数34为偏移量
case R.id.button04:
initPopupWindow(4);
mPopupWindow.showAtLocation(mRl_parent, Gravity.BOTTOM, 0, 0);
break;
case R.id.button05:
mIsClick5 = !mIsClick5;
initPopupWindow(5);
break;
default:
break;
}
}
}
2.activity_main.xml部分
<RelativeLayout android:id="@+id/rl_parent"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView
android:id="@+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:gravity="center"
android:text="popupwindow相对该view显示位置"/>
<TextView
android:id="@+id/tv_mainView"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:gravity="center"
android:text="我是MainView"
android:textColor="#333333"
android:background="#dddddd"
android:visibility="gone"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginBottom="18dp"
android:orientation="vertical">
<Button
android:id="@+id/button01"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="直接显示在某控件下方,不偏移且点击外面不可关闭"/>
<Button
android:id="@+id/button02"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="显示在某控件下方,有偏移且点击外面可关闭"/>
<Button
android:id="@+id/button03"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="相对父容器中心显示位置,不偏移且点击外面不可关闭"/>
<Button
android:id="@+id/button04"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="相对父容器脚部显示位置,下方中间且点击外面可关闭"/>
<Button
android:id="@+id/button05"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="直接使用view显示,随便设置弹出位置"/>
</LinearLayout>
</RelativeLayout>
3.popup_window.xml部分
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#dddddd"
android:orientation="vertical">
<TextView
android:id="@+id/tv_popupTv"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:layout_centerInParent="true"
android:gravity="center"
android:text="我是PopupWindow"
android:textColor="#333333"/>
</RelativeLayout>
4.popupwindow_enter.xml动画xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<!--位移动画:1为持续时长;2为X轴起始位置;3为X轴到达位置 ~看需求还可以添加设置Y轴位置-->
<!--X轴相对左右方向,Y轴相对上下方向-->
<translate
android:duration="500"
android:fromYDelta="-100"
android:toYDelta="0"/>
</set>
以上为相关代码。
源码下载链接:源码下载