昨天新界面UI出稿,看到了一个新需求:一个列表的item支持点击,点击Item在下方弹出弹窗显示详情。很好实现,用popupWindow即可,但同时也发现了一个问题,当点击下方item时,由于布局太靠底,导致悬浮框无法完全显示。想完全显示实现思路很多,比如可以使布局向下滚动,暴露出全体布局。楼主这里用到了另一种方案,如果当前布局没有充足空间显示悬浮框时,就在上方显示,具体实现也是比较简单的,封装了一个简单控件分享给大家,效果如下:

自适应空间的PopupWindow,动态修改显示位置(支持上下)


主要实现原理:悬浮框继承popupWindow,判断布局下方是否有足够空间显示,动态设置内部布局位置。由于悬浮框要求在item下显示,所以我定义了一个Baseview用来记记录点击位置的view并获取到x,y坐标,主要用到y坐标用来计算下方的空间大小。

首先定义一个三角形,楼主直接用xml写了,下面是正三角:

<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@+id/shape_id">
        <rotate
            android:fromDegrees="45"
            android:pivotX="-40%"
            android:pivotY="80%"
            android:toDegrees="45">
            <shape android:shape="rectangle">
                <solid android:color="@color/blue" />
            </shape>
        </rotate>
    </item>
</layer-list>

然后定义popupWindow的布局,其中rootView使用来放置悬浮框的容器,用相对布局是为了方便动态去设置内部子控件的位置:

<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>
        <variable
            name="presenter"
            type="com.wcong.adaptiondialog.AdaptionDialog.Presenter"/>
    </data>
    <RelativeLayout
        android:id="@+id/root_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#6631363f"
        android:onClick="@{()->presenter.onOutsideTouch()}"></RelativeLayout>
</layout>

初始化布局:

public class AdaptionDialog extends PopupWindow {

    private Context context;

    private View baseView;
    private View window;
    private View triangle;

    private LinearLayout windowLayout;

    private int baseViewLocation[];

    private DialogAdaptionBinding windowBinding;

    /**
     * @param context
     * @param baseView
     * @param resLayout
     */
    public AdaptionDialog(Context context, View baseView, @LayoutRes int resLayout
    ) {
        super(context);
        this.context = context;
        this.baseView = baseView;

        setBaseViewLocation();
        init(LayoutInflater.from(context).inflate(resLayout, null));
    }

    /**
     * @param context
     * @param baseView
     * @param windowView
     */
    public AdaptionDialog(Context context, View baseView, View windowView
    ) {
        super(context);
        this.context = context;
        this.baseView = baseView;

        setBaseViewLocation();
        init(windowView);
    }

    private void init(View windowView) {
        windowBinding = DataBindingUtil.inflate(LayoutInflater.from(context), R.layout.dialog_adaption,
                null, false);
        windowBinding.setPresenter(new Presenter());
        window = windowView;

        setWidth(ViewGroup.LayoutParams.MATCH_PARENT);
        setHeight(ViewGroup.LayoutParams.MATCH_PARENT);
        setBackgroundDrawable(new BitmapDrawable());
        setFocusable(true);
        setOutsideTouchable(true);

        //初始化三角形
        triangle = new View(context);
        triangle.setBackgroundDrawable(context.getResources().getDrawable(R.drawable.triangle));
        LinearLayout.LayoutParams triangleParams = new LinearLayout.LayoutParams(dip2px(10), dip2px(10));
        //可以动态去修改三角的左右边距,已达到想要的效果,默认15dp
        triangleParams.setMargins(dip2px(15), 0, 0, 0);
        triangle.setLayoutParams(triangleParams);

        //初始化用于放置悬浮框的layout,包含三角及提示框两个元素
        windowLayout = new LinearLayout(context);
        windowLayout.setOrientation(LinearLayout.VERTICAL);
        windowLayout.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
        windowLayout.setPadding(dip2px(10), dip2px(5), dip2px(10), dip2px(5));
        windowLayout.addView(triangle);
        windowLayout.addView(window);
        windowBinding.rootView.addView(windowLayout);

        setContentView(windowBinding.getRoot());

        getContentView().measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
    }

初始化与正常popupWindow相同,windowView即悬浮框布局,没有写死而用构造传递是为了小伙伴们可以导入自己的布局,windouLayout用来存放三角及悬浮框布局,然后把windowLayout add到rootView里,这时悬浮框已完成。


简单的逻辑来判断下方是否有足够空间,baseView距下方距离是否大于悬浮框高度:

/**
 * 判断下方是否有充足空间显示悬浮框
 *
 * @return
 */
private boolean isPlaced() {
    WindowManager windowManager = ((Activity) context).getWindowManager();
    Display display = windowManager.getDefaultDisplay();
    Point point = new Point();
    display.getSize(point);
    return point.y - (baseViewLocation[1] + baseView.getMeasuredHeight()) > getContentView().getMeasuredHeight();
}

动态修改windowLayout的布局位置,基于baseView的上下位置:

/**
 * 修改悬浮框位置
 *
 * @param view
 * @param x
 * @param y
 */
private void setLayout(View view, int x, int y) {
    ViewGroup.MarginLayoutParams margin = new ViewGroup.MarginLayoutParams(view.getLayoutParams());
    margin.setMargins(x, y, x + margin.width, y + margin.height);
    RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(margin);
    view.setLayoutParams(layoutParams);
}

public void showAtTop() {
    windowLayout.removeView(triangle);
    windowLayout.addView(triangle);
    triangle.setBackgroundDrawable(context.getResources().getDrawable(R.drawable.inverted_triangle));
    triangle.startAnimation(AnimationUtils.loadAnimation(context, R.anim.dialog_bottom_in));
    window.startAnimation(AnimationUtils.loadAnimation(context, R.anim.dialog_bottom_in));
    setLayout(windowLayout, 0, baseViewLocation[1] - getContentView().getMeasuredHeight());
    showAtLocation(baseView, Gravity.CENTER, 0, 0);
}

public void showAtBottom() {
    triangle.startAnimation(AnimationUtils.loadAnimation(context, R.anim.dialog_top_in));
    window.startAnimation(AnimationUtils.loadAnimation(context, R.anim.dialog_top_in));
    setLayout(windowLayout, 0, baseViewLocation[1] + baseView.getMeasuredHeight());
    showAtLocation(baseView, Gravity.CENTER, 0, 0);
}

在点击事件中使用:

holder.ll.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        View view = LayoutInflater.from(ListActivity.this).inflate(R.layout.layout_window, null);
        ((TextView) view.findViewById(R.id.txt_msg)).setText("This is " + data.get(position));
        new AdaptionDialog(ListActivity.this, v, view).show();
    }
});

实现还是很简单的,具体细节小伙伴可以查看Demo,Demo地址



相关文章:

  • 2022-12-23
  • 2021-10-14
  • 2022-12-23
  • 2021-11-17
  • 2021-06-27
  • 2021-07-14
  • 2022-12-23
  • 2021-10-24
猜你喜欢
  • 2021-05-18
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2021-11-26
相关资源
相似解决方案