如何实现类似Gmail应用程序的捏缩放行为?我已将标头容器放在ScrollView中,后跟WebView
。似乎是非常复杂的行为。
此处没有缩放。
当我们捏Webview时,上部容器按缩放比例向上滚动:
到目前为止,这是我的姓名缩写:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white"
android:fitsSystemWindows="true">
<android.support.design.widget.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/white">
</android.support.v7.widget.Toolbar>
</android.support.design.widget.AppBarLayout>
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/appbar">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="200dp"
android:background="@color/colorPrimary"></FrameLayout>
<WebView
android:id="@+id/webView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white"
android:scrollbars="none" />
</LinearLayout>
</ScrollView>
</RelativeLayout>
</FrameLayout>
答案 0 :(得分:3)
GMail使用启用了缩放缩放功能的Chrome WebView
。缩放仅适用于单线程视图。 WebSettings
setBuiltInZoomControls()默认为false
,而setDisplayZoomControls()默认为true
。通过同时更改缩放和缩放功能,将不会显示缩放控件:
webview.getSettings().setBuiltInZoomControls(true);
webview.getSettings().setDisplayZoomControls(false);
,该工具栏为透明样式ActionBar,样式为windowActionBarOverlay
,设置为true
:
标记,指示此窗口的操作栏是否应覆盖应用程序内容。
在最上方的滚动位置中删除了ActionBar的底部阴影。这个监听垂直滚动事件,而不监听任何缩放手势。这种效果大致是这样的(最初必须隐藏阴影):
webView.setOnScrollChangeListener(new View.OnScrollChangeListener() {
@Override
public void onScrollChange(View view, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
if(scrollY == 0) {
/* remove the ActionBar's bottom shadow */
} else {
/* apply the ActionBar's bottom shadow */
}
}
}
根据触发OnScrollChangeListener
的频率,甚至检查scrollY == 0
和scrollY == 1
可能已经足以打开和关闭阴影。
缩放时,这似乎是ScaleGestureDetector.SimpleOnScaleGestureListener
(请参见docs),其中.getScaleFactor()
用于设置辅助“工具栏”垂直顶部位置的动画,然后将其推向第二位置在可见视口之外。第二个“工具栏”似乎是嵌套的垂直DrawerLayout
-无法手动移动-这就是为什么它可以平滑移动的原因... DrawerLayout
不仅限于水平抽屉;我认为这就是答案。
Google在自己的应用中大量使用(支持库)androidx
类。假装这将是自定义实现,但这只是不了解给定标准行为的借口。
答案 1 :(得分:1)
我想我理解你的问题。扩展电子邮件时,您想要向上推动主题行,而其他电子邮件则向下推动。我试图实现在Gmail应用程序中显示电子邮件的想法。我认为我非常接近解决方案,因为推动不够顺利。但是,我想在这里分享答案,以表达我对您问题的看法。
I have created a GitHub repository,从中可以看到我的实施情况。我在那里也添加了一个自述文件来解释整个想法。
我尝试使用RecyclerView
具有不同的ViewType
来实现整个过程。我添加了如下适配器。
public class RecyclerViewWithHeaderFooterAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int HEADER_VIEW = 1;
private static final int GROUPED_VIEW = 2;
private static final int EXPANDED_VIEW = 3;
private ArrayList<Integer> positionTracker; // Take any list that matches your requirement.
private Context context;
private ZoomListener zoomListener;
// Define a constructor
public RecyclerViewWithHeaderFooterAdapter(Context context, ZoomListener zoomListener) {
this.context = context;
this.zoomListener = zoomListener;
positionTracker = Utilities.populatePositionsWithDummyData();
}
// Define a ViewHolder for Header view
public class HeaderViewHolder extends ViewHolder {
public HeaderViewHolder(View itemView) {
super(itemView);
itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Do whatever you want on clicking the item
}
});
}
}
// Define a ViewHolder for Expanded view
public class ExpandedViewHolder extends ViewHolder {
public ExpandedViewHolder(View itemView) {
super(itemView);
itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Do whatever you want on clicking the item
}
});
}
}
// Define a ViewHolder for Expanded view
public class GroupedViewHolder extends ViewHolder {
public GroupedViewHolder(View itemView) {
super(itemView);
itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Do whatever you want on clicking the item
}
});
}
}
// And now in onCreateViewHolder you have to pass the correct view
// while populating the list item.
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v;
if (viewType == EXPANDED_VIEW) {
v = LayoutInflater.from(context).inflate(R.layout.list_item_expanded, parent, false);
ExpandedViewHolder vh = new ExpandedViewHolder(v);
return vh;
} else if (viewType == HEADER_VIEW) {
v = LayoutInflater.from(context).inflate(R.layout.list_item_header, parent, false);
HeaderViewHolder vh = new HeaderViewHolder(v);
return vh;
} else {
v = LayoutInflater.from(context).inflate(R.layout.list_item_grouped, parent, false);
GroupedViewHolder vh = new GroupedViewHolder(v);
return vh;
}
}
// Now bind the ViewHolder in onBindViewHolder
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
try {
if (holder instanceof ExpandedViewHolder) {
ExpandedViewHolder vh = (ExpandedViewHolder) holder;
vh.bindExpandedView(position);
} else if (holder instanceof GroupedViewHolder) {
GroupedViewHolder vh = (GroupedViewHolder) holder;
} else if (holder instanceof HeaderViewHolder) {
HeaderViewHolder vh = (HeaderViewHolder) holder;
}
} catch (Exception e) {
e.printStackTrace();
}
}
// Now the critical part. You have return the exact item count of your list
// I've only one footer. So I returned data.size() + 1
// If you've multiple headers and footers, you've to return total count
// like, headers.size() + data.size() + footers.size()
@Override
public int getItemCount() {
return DEMO_LIST_SIZE; // Let us consider we have 6 elements. This can be replaced with email chain size
}
// Now define getItemViewType of your own.
@Override
public int getItemViewType(int position) {
if (positionTracker.get(position).equals(HEADER_VIEW)) {
// This is where we'll add the header.
return HEADER_VIEW;
} else if (positionTracker.get(position).equals(GROUPED_VIEW)) {
// This is where we'll add the header.
return GROUPED_VIEW;
} else if (positionTracker.get(position).equals(EXPANDED_VIEW)) {
// This is where we'll add the header.
return EXPANDED_VIEW;
}
return super.getItemViewType(position);
}
// So you're done with adding a footer and its action on onClick.
// Now set the default ViewHolder for NormalViewHolder
public class ViewHolder extends RecyclerView.ViewHolder {
// Define elements of a row here
public ViewHolder(View itemView) {
super(itemView);
// Find view by ID and initialize here
}
public void bindExpandedView(final int position) {
// bindExpandedView() method to implement actions
final WebView webView = itemView.findViewById(R.id.email_details_web_view);
webView.getSettings().setBuiltInZoomControls(true);
webView.getSettings().setDisplayZoomControls(false);
webView.loadUrl("file:///android_asset/sample.html");
webView.setOnScrollChangeListener(new View.OnScrollChangeListener() {
@Override
public void onScrollChange(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
zoomListener.onZoomListener(position);
}
});
}
}
}
展开的列表项包含一个WebView
,其中的包装器为wrap_content
。您将在list_item_expanded.xml
中找到以下布局。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<WebView
android:id="@+id/email_details_web_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/white"
android:scrollbars="none"
tools:ignore="WebViewLayout" />
</RelativeLayout>
我尝试为实验添加一些虚拟数据,因此编写了Utility
类。 RecyclerView
被设置为具有反向布局,因为这是在RecyclerView
中显示对话的普遍期望。
关键思想是scrollToPosition
展开时的WebView
。因此,感觉物品被向上和向下推以适应扩展。希望你能明白。
我将在此处添加一些屏幕截图,以使您了解到目前为止我可以实现的目标。
请注意,推动机构不平稳。我将为此工作。但是,我认为我应该在此处发布它,因为这可能会帮助您进行思考。我建议您clone the repository并运行该应用程序以检查整体实施情况。让我知道是否有任何反馈。
答案 2 :(得分:0)
我尝试通过覆盖webview.setBuiltInZoomControls()
并将所有运动事件提供给检测器来使用ScaleGestureDetector
和WebView
来实现此行为。它可以工作,但是比例检测器和缩放的工作原理有所不同,UX变得很糟糕。
如果您仔细查看Gmail缩放实现和Webview缩放,您会发现它们是不同的。我相信Gmail缩放基于view.setScaleX()
和view.setScaleY()
。您可以通过继承WebView
并遵循this guide来获得基本行为。您可能还需要呼叫view.setPivotX()
和view.setPivotY()
。 Gmail的实现较为复杂,因为它具有滚动功能,并且在放大时似乎可以向上滚动内容。您可以尝试使用一些实现可缩放容器并支持滚动的库,例如this one。但是我无法使其与WebView
一起正常工作。
总体而言,这是一项复杂的任务,您必须亲自尝试实现,以做出一些妥协并获得相似但不错的用户体验。