【问题标题】:PopupWindow out of screen when size is unspecified未指定大小时,PopupWindow 超出屏幕
【发布时间】:2011-10-08 10:38:39
【问题描述】:

那里的大多数示例都精确地指定了弹出窗口的宽度和高度。我希望它们是 WRAP_CONTENT - 因为内容是动态确定的,所以在构造函数中我为宽度和高度设置了 -2 并通过 showAsDropDown(View anchor)

显示它

这样做,弹出窗口总是绘制在锚视图下方,这意味着它可以在屏幕外绘制。下面的 sn -p 演示了这个问题。尝试单击最后一个 TextView,您将看不到任何 PopupWindow,因为它显示在窗口边界之外。为什么它不起作用?我注意到明确指定维度(例如 200、100)不会触发问题。自己试试吧

package com.zybnet.example.popupdemo;

import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.LinearLayout;
import android.widget.PopupWindow;
import android.widget.TextView;

public class PopupDemoActivity extends Activity implements OnClickListener {
    private PopupWindow popup;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // -2 means WRAP_CONTENT THIS TRIGGERS THE PROBLEM
        popup = new PopupWindow(getPopupContent(), -2, -2);
        // When you specify the dimensions everything goes fine
        //popup = new PopupWindow(getPopupContent(), 200, 100);

        LinearLayout layout = new LinearLayout(this);
        layout.setOrientation(LinearLayout.VERTICAL);

        // FILL_PARENT  and same layout weight for all children
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(-1, -1, 1);
        for (int i = 0; i < 10; i++) {
            TextView tv = new TextView(this);
            tv.setText("Click to show popup");
            tv.setOnClickListener(this);
            layout.addView(tv, params);
        }
        setContentView(layout);
    }

    @Override
    public void onClick(View view) {
        popup.dismiss();
        popup.showAsDropDown(view);
    }

    private View getPopupContent() {
        TextView popupContent = new TextView(this);
        popupContent.setText("Some text here");
        popupContent.setTextColor(Color.parseColor("#5000ae"));
        popupContent.setBackgroundColor(Color.parseColor("#ff00ff"));
        popupContent.setPadding(10, 20, 20, 10);
        return popupContent;
    }
}

【问题讨论】:

    标签: android


    【解决方案1】:

    我开始怀疑在 PopupWindow 的上下文中 -2, -2 实际上意味着 WRAP_CONTENT 而我认为它只是将其解释为宽度 = -2 高度 = -2

    这是来自 Android 源代码

    public PopupWindow(View contentView, int width, int height, boolean focusable) {
        if (contentView != null) {
            mContext = contentView.getContext();
            mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
        }
        setContentView(contentView);
        setWidth(width);
        setHeight(height);
        setFocusable(focusable);
    }
    

    其中setWidth(int width) 只是一个简单的mWidth = width; 我认为您正在寻找的是setWindowLayoutMode (int widthSpec, int heightSpec) 方法。那就是你应该传入WRAP_CONTENT

    如果所有其他方法都失败,请测量TextView 的宽度和高度,然后按预期使用setWidthsetHeight

    编辑

    刚刚自己试了一下,添加了这行代码让它工作

        // -2 means WRAP_CONTENT THIS TRIGGERS THE PROBLEM
        popup = new PopupWindow(getPopupContent(), 200, 100);
        popup.setWindowLayoutMode(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        // When you specify the dimensions everything goes fine
        //popup = new PopupWindow(getPopupContent(), 200, 100);
    
        LinearLayout layout = new LinearLayout(this);
        layout.setOrientation(LinearLayout.VERTICAL);
    

    我把原来的 cmets 留在了那里,所以你可以自己看看所有东西应该去哪里。

    编辑 2: 好的,所以我拿了您发布的代码并在我的设备上逐字尝试,您是对的,它不起作用,因为当 Android 确定要布局的视图时,它依赖于您提供的宽度和高度.这意味着,由于您使用 0 和 0 作为宽度和高度,Android 会出现并说:“哦,看,该框只是 0 x 0 框,因此我可以将其显示为内容下方的弹出窗口。”但这不是我们想要的!问题是,你知道这个盒子会比这更大,所以让我们开始输入一些不同的数字,看看它产生的结果。

    popup = new PopupWindow(getPopupContent(), 1, 1);
    

    现在,当我点击底部框时,请注意它会跳到上方。 (见截图)这是因为 Android 知道宽度和高度为 1(正如我在构造函数中设置的那样)并且列表项下方的可用屏幕空间为 0。好吧,如果没有足够的空间在那里显示它,那么它一定要在上面呢!

    但是等等!如果在我当前的示例中,字符串每次都添加更多内容怎么办?好吧,这就是事情变得有趣的地方。你看,在我的下一个屏幕截图中,即使它应该显示出来,因为它现在有 6 行长,但它却显示在底部!哦,废话,对!那是因为它是针对我在构造函数中使用的1 1 进行测量的。那么有什么解决办法呢?

    解决方案一:我的首选方法是猜测TextView 的平均最大高度是多少,然后简单地输入一个通常很大的数字。

    popup = new PopupWindow(getPopupContent(), 300, 300); //just guessing it won't get bigger than that
    

    解决方案二:这更合适,但是您要牺牲一点速度来做到这一点。在显示弹出窗口之前,使用Paint 类来测量文本内容的大小并将其传递到setWidth()setHeight()。我继续构建了一个几乎完整的解决方案,但我没有测量填充和其他东西(见评论)

    private int maxWidth;
    private int maxHeight;
    private Paint p = new Paint();
    private Rect bounds = new Rect();
    private View getPopupContent() {
        maxWidth = 0;
        maxHeight = 0;
        TextView popupContent = new TextView(this);
        popupContent.setText(popupText += "\n" + DEMO);
        popupContent.setTextColor(Color.parseColor("#5000ae"));
        popupContent.setBackgroundColor(Color.parseColor("#ff00ff"));
        popupContent.setPadding(10, 20, 20, 10);
        //the measure can only work line by line so I split it up by line
        String[] temp = popupText.split("\n");
        for (String s : temp){
            //measure each line of string and get its width and height
            p.getTextBounds(s, 0, s.length(), bounds);
    
            //keep a running total of the longest width
            maxWidth = (bounds.width() > maxWidth) ? bounds.width() : maxWidth;
            //add up each of the heights
            maxHeight += bounds.height();
        }
    
        //also take in account the padding too... if you REALLY want it to be completely robust
        //probably adding another 20 or 30 to the maxHeight should be good
        return popupContent;
    }
    

    然后在onClick()我添加了这两行

        popup.setHeight(maxHeight);
        popup.setWidth(maxWidth);
    

    【讨论】:

    • 我也阅读了源代码,这似乎更像是一个 Android 错误,因为大小是正确的,但位置是错误的。由于我提供了演示,您可以自己测试它。现在我正在试验 PopupWindow.showAtLocation()
    • 正如我怀疑您需要添加 setWindowLayoutMode 我编辑了我的答案并添加了您需要的确切工作代码。我在我的安卓设备和所有东西上测试了它! :)
    • 如果下次调用弹出窗口时内容视图发生变化会怎样?这就是为什么我需要 WRAP_CONTENT
    • 请再次阅读我的回答。我确实使用了 WRAP_CONTENT,它确实 WRAP_CONTENT 很好。我在我的 android 上运行了这段代码,它运行良好,所以请尝试一下,自己看看
    • 对不起,在我的测试中它不起作用。它仍然在屏幕外。这是一个演示 [链接] (pastebin.com/aFbzK3QM)
    【解决方案2】:

    我通过在弹出内容视图中调用measure() 方法解决了这个问题:

    View content = getPopupContent();
    
    content.measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
    int measuredHeight = content.getMeasuredHeight();
    
    popup = new PopupWindow(content, ViewGroup.LayoutParams.WRAP_CONTENT, measuredHeight);
    

    【讨论】:

      【解决方案3】:
      final PopupWindow popupWindow = new PopupWindow();
      // inflate your layout or dynamically add view
      LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
      View view = inflater.inflate(R.layout.alert_reply_chat,null);
      
      popupWindow.setFocusable(true);
      popupWindow.setWidth(WindowManager.LayoutParams.WRAP_CONTENT);
      popupWindow.setHeight(180);
      popupWindow.setBackgroundDrawable(null);
      
      popupWindow.setClippingEnabled(false);
      
      popupWindow.setTouchable(true);
      popupWindow.setContentView(view);
      
      return popupWindow;
      

      //上面是我检查过的工作代码

      【讨论】:

        猜你喜欢
        • 2021-09-25
        • 1970-01-01
        • 1970-01-01
        • 2021-07-18
        • 2011-09-01
        • 2020-12-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多