可拖动、可靠边的 popupWindow 实现

0 背景

  开发要实现一个可以拖动的圆角小窗,要求松手时,哪边近些靠哪边。并且还规定了拖动范围。样式如下:
可拖动、可靠边的 popupWindow 实现_第1张图片

1 实现

首先把 PopupWindow 的布局文件 pop.xml 实现


<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">
        "cornerFamily">rounded
        "cornerSize">12dp
    style>

    
    <style name="PopupWindowAnimation">
        "android:windowEnterAnimation">@anim/live_popup_window_in_anim
    style>

radius_12.xml


<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);
});

你可能感兴趣的:(Android实践,android)