安卓 - 底部行为子视图更改有时无法生效

4
在我的活动中,我有一个带有标记的地图。当用户单击标记时,它会显示有关该地点的详细信息并在底部面板中展示。
视图第一次填充有关位置的数据时,它可以正常工作。但是,在隐藏底部面板后再次点击后,它就无法正常工作: TextView保存旧高度(view.invalidate()不起作用)。 viewGroup.addView方法不起作用(在添加之前调用viewGroup.removeAllViews(),但它保留了旧高度,并且添加视图不起作用)。
但是,如果在不隐藏/显示bottomSheet的情况下更改视图数据,则所有事情都可以正常工作。就像第一次更改一样。
我在调试时看到其他奇怪的行为:我有一个包含4个RelativeLayout(内部带有TextView和ImageView)的LinearLayout。当我更改其中一个/两个的可见性(gone / visible)时,它们会重叠在一起,即使LinearLayout具有垂直方向。但并不总是这样,就像上面所述:第一次可以正常工作,其他时间如果在bottomSheet不可见时更改视图不起作用(如上所述),但是当bottomBehavior可见时,如果我更改视图,则可以正常工作(就像第一次一样)。
如果我没有弄错,这是由于bottomSheet。因为我之前没有在Android上看到过这样的视图行为。
我的代码:
mBottomSheetBehavior = BottomSheetBehavior.from(bottomSheetView);
    mBottomSheetBehavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
        @Override
        public void onStateChanged(@NonNull View bottomSheet, int newState) {
            switch (newState) {
                case BottomSheetBehavior.STATE_HIDDEN:
                    hidePlaceDetails();
                    break;
            }
        }

        @Override
        public void onSlide(@NonNull View bottomSheet, float slideOffset) {
        }
    });

/**
 * Show bottom sheet with given place details and start load route
 *
 * @param place the place
 */
private void showPlaceDetails(Place place) {
    placeTitleView.setText(place.getTitle());
    placeCategoryView.setText(place.getParentTitle());

    placeInfoGroup.removeAllViews();
    addPlaceInfo(R.drawable.ic_info, place.getDescription(), null);
    addPlaceInfo(R.drawable.ic_place, place.getAddress(), null);
    addPlaceInfo(R.drawable.ic_phone, place.getPhoneNumbers(), null);
    addPlaceInfo(R.drawable.ic_language, place.getWebsite(), null);

    Photo photo = place.getPhoto();
    if (U.isEmpty(photo.getImagePath())) {
        U.hideView(placePhotoView);
    } else {
        U.showView(placePhotoView);
        int proportionalHeight = U.calculateProportionalHeight(
            screenSize[0] /*screenWidth*/,
            photo.getWidth(),
            photo.getHeight(),
            800 /*maxHeight*/);
        FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(
            ViewGroup.LayoutParams.MATCH_PARENT,
            proportionalHeight
        );

        placePhotoView.setLayoutParams(layoutParams);
        ImageUtils.networkImage(this, photo, placePhotoView, null, U.imagePlaceholder());
    }

    U.view(placeDistanceProgressView, true); //showView
    U.view(placeDistanceView, false); //hideView
    if (mMap.getMyLocation() != null) {
        Waypoint myLocation = new Waypoint(mMap.getMyLocation().getLongitude(), mMap.getMyLocation().getLatitude());
        Waypoint destination = new Waypoint(place.getPosition().getLongitude(), place.getPosition().getLatitude());

        getAndDrawRoute(place, myLocation, destination);
    }

    int thirdScreen = screenSize[1] / 3;
    mMap.setPadding(0, 0, 0, halfScreen);
    isShowingPlaceDetails = true;
    mBottomSheetBehavior.setPeekHeight(halfScreen);
    mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
}

/**
 * Hide bottom sheet and add markers
 */
private void hidePlaceDetails() {
    mMap.setPadding(0, 0, 0, 0);
    mBottomSheetBehavior.setPeekHeight(0);
    mBottomSheetBehavior.setHideable(true);
    mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
    addMarkers();
    isShowingPlaceDetails = false;
}

/**
 * @param icon            icon res
 * @param title           title
 * @param onClickListener nullable
 */
private void addPlaceInfo(
    @DrawableRes int icon, String title, @Nullable View.OnClickListener onClickListener) {
    View root = LayoutInflater.from(this).inflate(R.layout.layout_map_bottom_sheet_row, null);
    ImageView iconView = ButterKnife.findById(root, R.id.place_icon);
    TextView titleView = ButterKnife.findById(root, R.id.place_title);
    iconView.setImageResource(icon);
    titleView.setText(U.notEmpty(title) ? title : Html.fromHtml(String.format("<i>%s</i>", "None")));

    if (onClickListener != null) {
        root.setOnClickListener(onClickListener);
    }
    placeInfoGroup.addView(root);
}

activity*.xml

<android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/drawer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">

<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <include layout="@layout/toolbar_appbar"/>

    <RelativeLayout
        android:id="@+id/content"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

        ...
    </RelativeLayout>

    <android.support.v4.widget.NestedScrollView
        android:id="@+id/bottomSheet"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:background="@android:color/white"
        android:elevation="4dp"
        app:behavior_peekHeight="0dp"
        app:layout_behavior="android.support.design.widget.BottomSheetBehavior">

        <include layout="@layout/layout_map_bottom_sheet"/>

    </android.support.v4.widget.NestedScrollView>

</android.support.design.widget.CoordinatorLayout>

...

layout_map_bottom_sheet.xml

<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white"
android:orientation="vertical"
android:paddingTop="@dimen/content.margin">

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:paddingLeft="@dimen/content.padding.big"
    android:paddingStart="@dimen/content.padding.big"
    android:paddingRight="@dimen/content.padding"
    android:paddingEnd="@dimen/content.padding">

    <TextView
        android:id="@+id/place.title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:lineSpacingExtra="@dimen/text.line.space"
        android:textColor="@color/text.color"
        android:textSize="@dimen/text.xlarge"
        tools:text="@string/lorem.place.title"/>

    <TextView
        android:id="@+id/place.category"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:lineSpacingExtra="@dimen/text.line.space"
        android:paddingBottom="@dimen/content.padding.small"
        android:paddingTop="@dimen/content.padding.small"
        android:textSize="@dimen/text.medium"
        android:textColor="@color/text.secondary.color"
        tools:text="@string/lorem.short"/>

    <include layout="@layout/divider"/>

    <TextView
        android:id="@+id/place.distance"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingBottom="@dimen/content.padding.small"
        android:paddingTop="@dimen/content.padding.small"
        android:textColor="?attr/colorPrimary"
        android:visibility="gone"
        tools:text="@string/lorem.place.distance"/>

    <ProgressBar
        android:id="@+id/place.distance.progress"
        android:layout_width="20dp"
        android:layout_height="20dp"
        android:layout_marginTop="@dimen/content.padding.small"
        android:layout_marginBottom="@dimen/content.padding.small"/>

    <include layout="@layout/divider"/>
</LinearLayout>

<LinearLayout
    android:id="@+id/place.infos"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">
</LinearLayout>

<FrameLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <ImageView
        android:id="@+id/place.photo"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:scaleType="centerCrop"
        tools:src="@drawable/sample"/>
</FrameLayout>

屏幕录制:https://drive.google.com/file/d/0B5up2CgQawSDbUZtQ2h1VDBWbms/view?usp=drivesdk

我很难解释这个问题,但这个问题让我疯狂了几天。如果您能提供任何帮助,我会非常感激!


你解决了这个问题吗?看起来我遇到了相同或非常相似的问题。当我使用removeAllViews时会出现这个问题。如果我不使用removeAllViews,那么它就能正常工作。 - peshkira
@peshkira 不,我放弃了。 - alashow
哦,糟糕 :/ ... 看起来这是与BottomSheet布局生命周期有关的问题,因为调试器显示视图已被插入。但是我却看不到它们。 - peshkira
@peshkira 对我来说,使用或不使用 removeAllViews 都没有关系。你试过 XML 截图吗?如果是的话,你能在那里看到它们吗? - alashow
是的,我刚试过了。有趣的是,支持库会将所有其他的TextView转换为AppCompatTextView,但动态添加的TextView仍然是TextView。而且它们的高度和宽度都为0。 - peshkira
1个回答

3

我用以下方法解决了类似的问题。

在我的 BottomSheetCallback 中,我添加了以下内容:

behavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
  @Override
  public void onStateChanged(final View bottomSheet, int newState) {
    switch (newState) {
      case BottomSheetBehavior.STATE_COLLAPSED:
      case BottomSheetBehavior.STATE_EXPANDED:
        bottomSheet.post(new Runnable() {
          @Override
          public void run() {
            bottomSheet.requestLayout(); // this seems to fix it.
          }
        });
        break;
      case BottomSheetBehavior.STATE_HIDDEN:
        // remove the views here with removeAllViews();
        break;
    }
  }

  @Override
  public void onSlide(View bottomSheet, float slideOffset) {

  }
});

这还不够完美,因为新的布局需要一些时间来渲染,视图会有一些延迟。但总比没有好。

此外,如果您已经支持目标版本24,则最新的支持库24.0.0中似乎有一个修复此问题的bug。

https://code.google.com/p/android/issues/detail?id=205226


我尝试过使布局失效,但没有成功。但我没有尝试在展开状态下使用 post 运行来使其失效。我改变了我的布局结构,所以无法测试您的解决方案是否有效。如果其他用户投票支持您的解决方案,我将接受它作为解决方案。谢谢! - alashow
请注意,invalidaterequestLayout不是相同的操作。invalidate仅标记布局为脏状态,而requestLayout则会安排进行新的布局处理。 - peshkira
1
这个解决方案对我来说很好用。但是当折叠底部表单时,我发现视图会出现滞后。 - Akhil Soni

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