0 背景
开发要实现一个可以拖动的圆角小窗,要求松手时,哪边近些靠哪边。并且还规定了拖动范围。样式如下:
1 实现
首先把 PopupWindow 的布局文件 pop.xml 实现
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="88dp"
android:layout_height="132dp"
android:background="@drawable/radius_12"
android:id="@+id/mini_popup"
android:visibility="visible">
<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/iv_live_cover"
android:layout_width="88dp"
android:scaleType="fitXY"
android:layout_height="132dp"
android:background="@color/purple_200"
app:shapeAppearanceOverlay="@style/MiniDialogRoundedImageStyle" />
<ImageView
android:id="@+id/iv_close"
android:layout_width="16dp"
android:layout_height="16dp"
android:layout_alignParentRight="true"
android:layout_marginTop="4dp"
android:layout_marginRight="4dp"
android:src="@color/teal_200" />
</RelativeLayout>
布局中圆角和 PopupWindow 的动画 style.xml
<!-- 圆角图片 -->
<style name="MiniDialogRoundedImageStyle">
<item name="cornerFamily">rounded</item>
<item name="cornerSize">12dp</item>
</style>
<!-- PopupWindow 的动画效果 -->
<style name="PopupWindowAnimation">
<item name="android:windowEnterAnimation">@anim/live_popup_window_in_anim</item>
</style>
radius_12.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="12dp"/>
<solid android:color="@color/white"/>
</shape>
MyPopupWindow.java
package com.example.myapplication.popupwindow;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.res.Resources;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
import android.widget.PopupWindow;
import com.bumptech.glide.Glide;
import com.example.myapplication.R;
public class MyPopupWindow extends PopupWindow {
private Context mContext;
private View mRootView;
// 背景
private ImageView mBackground;
// 关闭弹窗
private ImageView mIvClose;
// 弹窗的移动范围
private int mMinX;
private int mMinY;
private int mMaxX;
private int mMaxY;
// 屏幕宽高
private int mScreenWidth;
public MyPopupWindow(Context context) {
super(context);
mContext = context;
mRootView = View.inflate(mContext, R.layout.pop, null);
mScreenWidth = getScreenWidth(mContext);
mMinX = dp2px(12);
mMaxX = mScreenWidth - dp2px(12) - dp2px(88);
mMinY = dp2px(12);
mMaxY = dp2px(500);
// 为了保证整体是圆角形状
mRootView.findViewById(R.id.mini_popup).setClipToOutline(true);
initView();
}
private void initView() {
setContentView(mRootView);
mBackground = mRootView.findViewById(R.id.iv_live_cover);
mIvClose = mRootView.findViewById(R.id.iv_close);
mIvClose.setOnClickListener(view -> this.dismiss());
// 小窗的宽高
setHeight(dp2px(132));
setWidth(dp2px(88));
this.setTouchInterceptor(new View.OnTouchListener() {
int orgX, orgY;
int offsetX, offsetY;
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
switch (motionEvent.getAction()) {
case MotionEvent.ACTION_DOWN:
orgX = (int) motionEvent.getX();
orgY = (int) motionEvent.getY();
break;
case MotionEvent.ACTION_MOVE:
offsetX = (int) motionEvent.getRawX() - orgX;
offsetY = (int) motionEvent.getRawY() - orgY;
// 限制 x 坐标
offsetX = Math.max(offsetX, mMinX);
offsetX = Math.min(offsetX, mMaxX);
// 限制 y 坐标
offsetY = Math.max(offsetY, mMinY);
offsetY = Math.min(offsetY, mMaxY);
update(offsetX, offsetY, -1, -1, true);
break;
case MotionEvent.ACTION_UP:
// 小窗靠边
if (offsetX < mScreenWidth / 2) {
offsetX = mMinX;
} else {
offsetX = mMaxX;
}
update(offsetX, offsetY, -1, -1, true);
break;
}
// 避免 view 中的其他点击事件被吞掉
return false;
}
});
// 设置小窗背景
this.setBackgroundDrawable(mContext.getResources().getDrawable(R.drawable.abc_vector_test));
// 出现的动画
this.setAnimationStyle(R.style.PopupWindowAnimation);
}
public void show(View anchor) {
this.showAtLocation(anchor, Gravity.NO_GRAVITY, mMaxX, mMaxY);
}
@SuppressLint("CheckResult")
public void setBackground(String url) {
if (url != null && !TextUtils.isEmpty(url))
Glide.with(mContext).load(url).into(mBackground);
}
public int dp2px(float dpValue) {
return (int) (0.5f + dpValue * Resources.getSystem().getDisplayMetrics().density);
}
public int getScreenWidth(Context context) {
DisplayMetrics localDisplayMetrics = new DisplayMetrics();
((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(localDisplayMetrics);
return localDisplayMetrics.widthPixels;
}
}
最后在 MainActivity 中使用
mTextView = findViewById(R.id.myView);
if (mMyPopupWindow == null) {
mMyPopupWindow = new MyPopupWindow(MainActivity.this);
}
mTextView.post(() -> {
mMyPopupWindow.show(mTextView);
});