背景
我被指派创建一个 UI,其行为类似于 Google map 为找到的结果显示底页的方式。
它分为三个不同的阶段:
- 底部内容。上部区域仍然是可触摸的,并且不会在底部滚动任何内容
- 全屏内容,而上部区域有一个大标题。
- 全屏内容,而上方区域只有工具栏。
这就是我在 Google map 上所说的内容:
问题
问题是,底部的工作表还不是设计库的一部分(尽管它被请求了,here)。
不仅如此,UI 看起来也相当复杂,需要在多个阶段处理工具栏。
我尝试过的
我为底部工作表(here)找到了一个很好(足够)的库,并将内容添加到其 fragment 示例中,以具有与 Material 设计示例中显示的 View 大致相同的 View (如 here),拥有一个负责处理 2+3 阶段的 CollapsingToolbarLayout。
在我正在制作的应用程序中,我还必须在滚动时移动一个图标,但我认为如果我成功完成了其余部分,这应该很容易。 代码如下:
###fragment_my.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
android:id="@+id/main_content"
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.support.design.widget.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="@dimen/detail_backdrop_height"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
<android.support.design.widget.CollapsingToolbarLayout
android:id="@+id/collapsing_toolbar"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:contentScrim="?attr/colorPrimary"
app:expandedTitleMarginEnd="64dp"
app:expandedTitleMarginStart="48dp"
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap">
<ImageView
android:id="@+id/backdrop"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
app:layout_collapseMode="parallax"/>
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_collapseMode="pin"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingTop="24dp">
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/card_margin">
<LinearLayout
style="@style/Widget.CardContent"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Info"
android:textAppearance="@style/TextAppearance.AppCompat.Title"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/cheese_ipsum"/>
</LinearLayout>
</android.support.v7.widget.CardView>
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/card_margin"
android:layout_marginLeft="@dimen/card_margin"
android:layout_marginRight="@dimen/card_margin">
<LinearLayout
style="@style/Widget.CardContent"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Friends"
android:textAppearance="@style/TextAppearance.AppCompat.Title"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/cheese_ipsum"/>
</LinearLayout>
</android.support.v7.widget.CardView>
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/card_margin"
android:layout_marginLeft="@dimen/card_margin"
android:layout_marginRight="@dimen/card_margin">
<LinearLayout
style="@style/Widget.CardContent"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Related"
android:textAppearance="@style/TextAppearance.AppCompat.Title"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/cheese_ipsum"/>
</LinearLayout>
</android.support.v7.widget.CardView>
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
<android.support.design.widget.FloatingActionButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/fab_margin"
android:clickable="true"
android:src="@android:drawable/ic_menu_send"
app:layout_anchor="@id/appbar"
app:layout_anchorGravity="bottom|right|end"/>
</android.support.design.widget.CoordinatorLayout>
###MyFragment.java
public class MyFragment extends BottomSheetFragment {
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
final View view = inflater.inflate(R.layout.fragment_my, container, false);
view.setMinimumHeight(getResources().getDisplayMetrics().heightPixels);
CollapsingToolbarLayout collapsingToolbar = (CollapsingToolbarLayout) view.findViewById(R.id.collapsing_toolbar);
collapsingToolbar.setTitle("AAA");
final Toolbar toolbar = (Toolbar) view.findViewById(R.id.toolbar);
final AppCompatActivity activity = (AppCompatActivity) getActivity();
activity.setSupportActionBar(toolbar);
activity.getSupportActionBar().setDisplayHomeAsUpEnabled(true);
//toolbar.setNavigationIcon(R.drawable.abc_ic_ab_back_mtrl_am_alpha);
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
NavUtils.navigateUpFromSameTask(getActivity());
}
});
final ImageView imageView = (ImageView) view.findViewById(R.id.backdrop);
Glide.with(this).load(R.drawable.cheese_1).centerCrop().into(imageView);
return view;
}
}
###BottomSheetFragmentActivity.java
public final class BottomSheetFragmentActivity extends AppCompatActivity {
protected BottomSheetLayout bottomSheetLayout;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_bottom_sheet_fragment);
bottomSheetLayout = (BottomSheetLayout) findViewById(R.id.bottomsheet);
findViewById(R.id.bottomsheet_fragment_button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new MyFragment().show(getSupportFragmentManager(), R.id.bottomsheet);
}
});
bottomSheetLayout.setShouldDimContentView(false);
bottomSheetLayout.setPeekOnDismiss(true);
bottomSheetLayout.setPeekSheetTranslation(200);
bottomSheetLayout.setInterceptContentTouch(false);
bottomSheetLayout.setDefaultViewTransformer(new BaseViewTransformer() {
@Override
public void transformView(final float translation, final float maxTranslation, final float peekedTranslation, final BottomSheetLayout parent, final View view) {
Log.d("AppLog", "translation:" + translation + " maxTranslation:" + maxTranslation + " peekedTranslation:" + peekedTranslation);
}
});
}
}
它几乎可以正常工作。唯一的问题是从 #3 到 #2 的过渡:
问题
代码有什么问题?为了实现所需的行为,我该怎么做?
最佳答案
注意:阅读底部的修改
好的,我已经找到了一种方法,但是我必须更改多个类的代码,以便底部工作表知道 appBarLayout 的状态(扩展与否),并忽略向上滚动如果没有展开:
BottomSheetLayout.java
添加字段:
private AppBarLayout mAppBarLayout;
private OnOffsetChangedListener mOnOffsetChangedListener;
private int mAppBarLayoutOffset;
init() - 添加了这个:
mOnOffsetChangedListener = new OnOffsetChangedListener() {
@Override
public void onOffsetChanged(final AppBarLayout appBarLayout, final int verticalOffset) {
mAppBarLayoutOffset = verticalOffset;
}
};
增加了设置appBarLayout的功能:
public void setAppBarLayout(final AppBarLayout appBarLayout) {
if (mAppBarLayout == appBarLayout)
return;
if (mAppBarLayout != null)
mAppBarLayout.removeOnOffsetChangedListener(mOnOffsetChangedListener);
mAppBarLayout = appBarLayout;
mAppBarLayout.addOnOffsetChangedListener(mOnOffsetChangedListener);
}
onDetachedFromWindow() - 添加了这个:
if (mAppBarLayout != null)
mAppBarLayout.removeOnOffsetChangedListener(mOnOffsetChangedListener);
onTouchEvent() - 添加了这个:
...
if (bottomSheetOwnsTouch) {
if (state == State.EXPANDED && scrollingDown && mAppBarLayout != null && mAppBarLayoutOffset != 0) {
event.offsetLocation(0, sheetTranslation - getHeight());
getSheetView().dispatchTouchEvent(event);
return true;
}
...
这些是主要的变化。现在是什么让他们:
MyFragment.java
onCreateView() - 添加了这个:
mBottomSheetLayout.setAppBarLayout((AppBarLayout) view.findViewById(R.id.appbar));
我也添加了这个功能:
public void setBottomSheetLayout(final BottomSheetLayout bottomSheetLayout) {
mBottomSheetLayout = bottomSheetLayout;
}
现在这是 Activity 告诉 fragment 有关 appBarLayout 的方式:
final MyFragment myFragment = new MyFragment();
myFragment.setBottomSheetLayout(bottomSheetLayout);
myFragment.show(getSupportFragmentManager(), R.id.bottomsheet);
该项目现已在 GitHub 上可用:
https://github.com/AndroidDeveloperLB/ThreePhasesBottomSheet
我希望它没有任何错误。
遗憾的是,该解决方案存在错误,因此我不会将此答案标记为正确答案:
- 它仅适用于 Android 6 及更高版本。其他人则有一种奇怪的行为,即每次显示底部工作表时都会扩展一小部分时间。
- 方向更改根本不保存滚动状态,所以我禁用了它。
- 能够在底部工作表的内容仍处于折叠状态时(在底部)滚动的罕见问题
- 如果之前显示过键盘,则在尝试查看时,底部工作表可能会全屏显示。
如果有人可以帮忙,请帮忙。
对于问题 #1,我尝试通过将可见性设置为 INVISIBLE 来添加修复,当底部工作表还没有窥视时,但它并不总是有效,尤其是在显示键盘的情况下。
对于问题 #1,我找到了解决方法,只需将 CoordinatorLayout 包装(在“fragment_my.xml”中)与您希望使用的任何 View (我使用 FrameLayout),并放置一个完整的 -大小的 View (我只是放了“ View ”),例如:
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!--This full sized view, together with the FrameLayout above, are used to handle some weird UI issues on pre-Android-6 -->
<View
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<...CollapsingToolbarLayout
...
当我将 CoordinatorLayout 作为它的 View 时,它可能混淆了 bottomSheet。 我已经更新了项目,但是,如果有任何方法可以提供更好的解决方案,我想知道它。
最近几个月,谷歌发布了自己的 bottomSheet 类,但我发现它有很多问题,所以我什至无法尝试。
关于android - 如何模仿谷歌地图的底页 3 阶段行为?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34160423/