Android:SlidingDrawer的高度可以使用wrap_content设置吗?

56
我正在尝试实现一个 SlidingDrawer,它将占用整个屏幕宽度,但其高度由其内容动态确定:换句话说,宽度采用标准的 fill_parent 布局行为,而高度则采用 wrap_content。这正是我在布局XML中指定的方式(请参见下面的代码),但是抽屉总是打开到全屏高度。我的内容高度不同,但通常只有屏幕高度的一半左右,因此我就会得到一个大的空白区域。我希望内容能整齐地放在屏幕底部。
我已经尝试了我能想到的所有方法来解决问题,但到目前为止没有什么作用。如果我将 SlidingDrawerlayout_height 设置为特定值(例如160dip),它能够工作,但这不是我需要的:它必须是动态的。当然,我已经确保所有子元素的高度也都设置为 wrap_content
关于 SlidingDrawer 的文档有点含糊不清,我也没有找到任何例子可以满足我的需求。如果有人能看出我错在哪里,我会非常感激您的帮助!
<RelativeLayout
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" >

    <ViewFlipper
        android:id="@+id/ImageFlipper"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" >

        <ImageView
            android:id="@+id/imageView0"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:scaleType="centerCrop" />

        <ImageView
            android:id="@+id/imageView1"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:scaleType="centerCrop" />

        <ImageView
            android:id="@+id/imageView2"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:scaleType="centerCrop" />

    </ViewFlipper>

    <SlidingDrawer
        android:id="@+id/infoDrawer"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:handle="@+id/infoDrawerHandle"
        android:content="@+id/infoDrawerContent"
        android:allowSingleTap="false"
        android:layout_alignParentBottom="true"
        android:orientation="vertical" >

        <!-- Sliding drawer handle -->
        <ImageView
            android:id="@id/infoDrawerHandle"
            android:src="@drawable/info_handle_closed"
            android:layout_width="wrap_content" 
            android:layout_height="wrap_content" />

        <!-- Sliding drawer content: a scroller containing a group of text views
        laid out in a LinearLayout -->
        <ScrollView
            android:id="@id/infoDrawerContent"
            android:background="@drawable/info_background"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:fillViewport="false" >

            <LinearLayout
                android:id="@id/infoDrawerContent"
                android:orientation="vertical"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:paddingRight="5dip" >

                <TextView
                    android:id="@+id/infoTitle"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:textColor="#ffffff"
                    android:textSize="16dip"
                    android:textStyle="bold" />

                <TextView
                    android:id="@+id/infoCreator"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:textColor="#ffffff"
                    android:textSize="14dip"
                    android:textStyle="italic"
                    android:paddingBottom="10dip" />

                <TextView
                    android:id="@+id/infoDescription"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:textColor="#ffffff"
                    android:textSize="14dip"
                    android:paddingBottom="10dip" />

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:textColor="#ffcc00"
                    android:textSize="14dip"
                    android:textStyle="bold"
                    android:text="@string/heading_pro_tip" />

                <TextView
                    android:id="@+id/infoProTip"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:textColor="#ffcc00"
                    android:textSize="14dip" />

            </LinearLayout>    

        </ScrollView>

    </SlidingDrawer>

</RelativeLayout>
6个回答

127
SlidingDrawer类的onMeasure()方法基本上会覆盖布局模式为fill_parent,这就是为什么layout_height="wrap_content"不起作用的原因。
要解决这个问题,您可以扩展SlidingDrawer类,并重新实现onMeasure()方法来遵守layout_widthlayout_height属性。然后,在您的XML布局中使用此自定义类,将<SlidingDrawer ...>替换为<fully.qualified.package.ClassName ...>
请注意,由于抽屉不再填充父布局,因此您必须将其置于LinearLayout中,并将gravity属性设置为抽屉所在的边缘。
以下是我为此目的创建的类和示例布局。 WrappingSlidingDrawer类:
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.SlidingDrawer;


public class WrappingSlidingDrawer extends SlidingDrawer {

    public WrappingSlidingDrawer(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);

        int orientation = attrs.getAttributeIntValue("android", "orientation", ORIENTATION_VERTICAL);
        mTopOffset = attrs.getAttributeIntValue("android", "topOffset", 0);
        mVertical = (orientation == SlidingDrawer.ORIENTATION_VERTICAL);
    }

    public WrappingSlidingDrawer(Context context, AttributeSet attrs) {
        super(context, attrs);

        int orientation = attrs.getAttributeIntValue("android", "orientation", ORIENTATION_VERTICAL);
        mTopOffset = attrs.getAttributeIntValue("android", "topOffset", 0);
        mVertical = (orientation == SlidingDrawer.ORIENTATION_VERTICAL);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSpecSize =  MeasureSpec.getSize(widthMeasureSpec);

        int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSpecSize =  MeasureSpec.getSize(heightMeasureSpec);

        if (widthSpecMode == MeasureSpec.UNSPECIFIED || heightSpecMode == MeasureSpec.UNSPECIFIED) {
            throw new RuntimeException("SlidingDrawer cannot have UNSPECIFIED dimensions");
        }

        final View handle = getHandle();
        final View content = getContent();
        measureChild(handle, widthMeasureSpec, heightMeasureSpec);

        if (mVertical) {
            int height = heightSpecSize - handle.getMeasuredHeight() - mTopOffset;
            content.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(height, heightSpecMode));
            heightSpecSize = handle.getMeasuredHeight() + mTopOffset + content.getMeasuredHeight();
            widthSpecSize = content.getMeasuredWidth();
            if (handle.getMeasuredWidth() > widthSpecSize) widthSpecSize = handle.getMeasuredWidth();
        }
        else {
            int width = widthSpecSize - handle.getMeasuredWidth() - mTopOffset;
            getContent().measure(MeasureSpec.makeMeasureSpec(width, widthSpecMode), heightMeasureSpec);
            widthSpecSize = handle.getMeasuredWidth() + mTopOffset + content.getMeasuredWidth();
            heightSpecSize = content.getMeasuredHeight();
            if (handle.getMeasuredHeight() > heightSpecSize) heightSpecSize = handle.getMeasuredHeight();
        }

        setMeasuredDimension(widthSpecSize, heightSpecSize);
    }

    private boolean mVertical;
    private int mTopOffset;
}

示例布局(假设 WrappingSlidingDrawer 在包 com.package 中):

<FrameLayout android:layout_width="fill_parent"
             android:layout_height="fill_parent">
    ... stuff you want to cover at full-size ...
    <LinearLayout android:layout_width="fill_parent"
              android:layout_height="fill_parent"
              android:gravity="bottom"
              android:orientation="vertical">
        <com.package.WrappingSlidingDrawer android:layout_width="fill_parent"
                           android:layout_height="wrap_content"
                           android:content="@+id/content"
                           android:handle="@+id/handle">
            ... handle and content views ...
        </com.package.WrappingSlidingDrawer>
    </LinearLayout>
</FrameLayout>

太棒了!我为这个滑动抽屉问题苦苦挣扎了几周。非常感谢! - Gratzi
太棒了:感谢您提供如此详细的答案,很抱歉我花了这么长时间才接受它! - Mark Whitaker
这很棒,但是我怎样才能使某个东西对齐并填充整个屏幕直到手柄的位置呢?现在的工作方式是,线性布局具有填充宽度和高度的功能,因此我无法在屏幕上放置其他任何东西... 有什么提示吗?我想要的只是一个覆盖整个屏幕的元素,并在其底部放置一个只打开所需内容的手柄...换句话说-我想将一个元素与手柄以上对齐-使用此方法是否可能? - ekatz
+1 - 这正是我所需要的。通过将layout_gravity分配给WrappingSlidingDrawer,我能够避免需要一个封闭的LinearLayout。 - Tom
@seydhe:非常感谢,它解决了我的问题。 :) - Ajay
显示剩余6条评论

5

seydhe的回答有一个小问题。

getAttributeIntValue的第一个参数需要是完整的命名空间,而不仅仅是“android”。因此,代码片段应该是:

final String xmlns="http://schemas.android.com/apk/res/android";
int orientation = attrs.getAttributeIntValue(xmlns, "orientation", SlidingDrawer.ORIENTATION_VERTICAL);
 mTopOffset = attrs.getAttributeIntValue(xmlns, "topOffset", 0);

我曾经遇到过一个问题,使用水平滑动抽屉时无法正常工作,后来发现它无法找到方向属性,因此将其视为垂直方向。

这不应该是一个答案。建议进行编辑。 - Shubham A.

5
只需在XML文件中将滑动抽屉的pmargin设为所需值即可。
android:layout_marginTop="50dip"

4

最好不要在程序中硬编码字符串,而是将其作为参数读取:

    int attrOrientation = android.R.attr.orientation;
    int attrTopOffset = android.R.attr.topOffset;

    int[] attrIds = new int [] {attrOrientation, attrTopOffset}; 

    TypedArray a = context.obtainStyledAttributes(attrs, attrIds);
    int orientation = a.getInt(0, SlidingDrawer.ORIENTATION_VERTICAL);
    topOffset = a.getDimension(1, 0);
    a.recycle(); 

    isVertical = (orientation == SlidingDrawer.ORIENTATION_VERTICAL);

另一个问题在于onMeasure函数中。我使用了以下代码:
    if (isVertical) {
        int height = heightSpecSize - handle.getMeasuredHeight() - topOffset;
        getContent().measure(MeasureSpec.makeMeasureSpec(widthSpecSize, MeasureSpec.UNSPECIFIED), MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
        heightSpecSize = handle.getMeasuredHeight() + topOffset + content.getMeasuredHeight();
        widthSpecSize = content.getMeasuredWidth();
        if (handle.getMeasuredWidth() > widthSpecSize) widthSpecSize = handle.getMeasuredWidth();
    } else {
        int width = widthSpecSize - handle.getMeasuredWidth() - topOffset;
        getContent().measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(heightSpecSize, MeasureSpec.UNSPECIFIED));
        widthSpecSize = handle.getMeasuredWidth() + topOffset + content.getMeasuredWidth();
        heightSpecSize = content.getMeasuredHeight();
        if (handle.getMeasuredHeight() > heightSpecSize) heightSpecSize = handle.getMeasuredHeight();
    }

2
+1 - 我使用了第一个改进来避免硬编码。我需要做的一个小改变是如何获取topOffset值:topOffset =(int)a.getDimension(1,0); - Tom

2

很遗憾,您不能设置高度,而是相反的。topOffset属性将确定滑动抽屉的高度,但它是要刮掉多少而不是它将有多高。


实际上,我已经成功设置了SlidingDrawer的高度(如我上面提到的),通过为SlidingDrawer指定一个明确的高度:看起来topOffset解决方案是实现相同效果的另一种方式。(我也找到了一些这样做的例子,但迄今为止没有使用wrap_content的例子。) - Mark Whitaker
这个人的面板小部件可以让你设置高度为wrap_content,还有其他不错的功能。http://www.anddev.org/viewtopic.php?p=16622 - Nathan Schwermann

0

对我来说它有效:

private SlidingDrawer rightSlidingPanel = null;

 @Override 
public void onCreate( Bundle savedInstanceState )
{
...
    rightSlidingPanel = (SlidingDrawer) findViewById( R.id.rightSlidingPanel );
    rightSlidingPanel.post( new Runnable() 
            {
                @Override
                public void run()
                {
                    rightSlidingPanel.getLayoutParams().width = findViewById( R.id.sliding_content2 ).getMeasuredWidth() + findViewById( R.id.sliding_handle ).getMeasuredWidth();
                }

            });
}

XML布局:

...
    <SlidingDrawer
            android:id="@+id/rightSlidingPanel"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_alignParentRight="true"
            android:layout_alignParentTop="true"
            android:allowSingleTap="true"
            android:animateOnClick="true"
            android:content="@+id/sliding_content"
            android:handle="@+id/sliding_handle"
            android:orientation="horizontal" >

            <Button
                android:id="@+id/sliding_handle"
                style="@style/toolbar_button"
                android:layout_width="30dp"
                android:layout_height="wrap_content"
                android:height="40dp"
                android:text="&lt;"
                android:width="25dp" />

            <LinearLayout
                android:id="@+id/sliding_content"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:gravity="top"
                android:orientation="vertical" >

                <LinearLayout
                    android:id="@+id/sliding_content2"
                    android:layout_width="wrap_content"
                    android:layout_height="match_parent"
                    android:layout_gravity="center_vertical"
                    android:layout_weight="1"
                    android:gravity="center_horizontal" >

...
                </LinearLayout>
            </LinearLayout>
        </SlidingDrawer>
...

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接