ツールバーの展開/折りたたみアニメーションがどのように行われるかを把握しようとしています。 Telegramアプリの設定を見ると、リストビューとツールバーがあることがわかります。下にスクロールするとツールバーが折りたたまれ、上にスクロールすると展開されます。プロファイル写真とFABのアニメーションもあります。誰にもそれについての手がかりがありますか?彼らはその上にすべてのアニメーションを構築したと思いますか?たぶん、新しいAPIやサポートライブラリから何かが欠けているのかもしれません。
Googleカレンダーアプリでも、スピナーを開くと同じ動作に気付きました(スピナーではないと思いますが、見た目は似ています)。ツールバーが拡大し、上にスクロールすると折りたたまれます。
明確にするために、QuickReturnメソッドは必要ありません。おそらく、Telegramアプリが類似のものを使用していることを知っています。必要な正確な方法は、Googleカレンダーアプリの効果です。私が試した
Android:animateLayoutChanges="true"
そして、エキスパンド方法はかなりうまく機能します。しかし、明らかに、ListViewを上にスクロールしても、ツールバーは折りたたまれません。
GestureListener
を追加することも考えましたが、これを達成するためのAPIまたはより簡単な方法があるかどうかを知りたいです。
ない場合は、GestureListener
を使用すると思います。うまくいけば、アニメーションの効果がスムーズになります。
ありがとう!
編集:
Android Designサポートライブラリのリリース以来、より簡単な解決策があります。チェック joaquin's answer
-
ここに私がそれをした方法があります、おそらく他の多くの解決策がありますが、これは私のために働いた。
まず、透明な背景を持つToolbar
を使用する必要があります。展開と折りたたみToolbar
は、実際には、透明なToolbar
の下にあるfakeの1つです。 (下の最初のスクリーンショット-マージンのあるスクリーンショット-で、これがTelegramでも同じであることがわかります)。
Toolbar
とオーバーフローNavigationIcon
の実際のMenuItem
のみを保持します。
2番目のスクリーンショットの赤い長方形にあるものすべて(つまり、偽のToolbar
とFloatingActionButton
)は、実際にはヘッダー設定に追加しますListView
(またはScrollView
)。
したがって、次のような別のファイルにこのヘッダーのレイアウトを作成する必要があります。
<!-- The headerView layout. Includes the fake Toolbar & the FloatingActionButton -->
<FrameLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
Android:layout_width="match_parent"
Android:layout_height="wrap_content">
<RelativeLayout
Android:id="@+id/header_container"
Android:layout_width="match_parent"
Android:layout_height="@dimen/header_height"
Android:layout_marginBottom="3dp"
Android:background="@Android:color/holo_blue_dark">
<RelativeLayout
Android:id="@+id/header_infos_container"
Android:layout_width="match_parent"
Android:layout_height="wrap_content"
Android:layout_alignParentBottom="true"
Android:padding="16dp">
<ImageView
Android:id="@+id/header_picture"
Android:layout_width="wrap_content"
Android:layout_height="wrap_content"
Android:layout_centerVertical="true"
Android:layout_marginRight="8dp"
Android:src="@Android:drawable/ic_dialog_info" />
<TextView
Android:id="@+id/header_title"
style="@style/TextAppearance.AppCompat.Title"
Android:layout_width="wrap_content"
Android:layout_height="wrap_content"
Android:layout_toRightOf="@+id/header_picture"
Android:text="Toolbar Title"
Android:textColor="@Android:color/white" />
<TextView
Android:id="@+id/header_subtitle"
style="@style/TextAppearance.AppCompat.Subhead"
Android:layout_width="wrap_content"
Android:layout_height="wrap_content"
Android:layout_below="@+id/header_title"
Android:layout_toRightOf="@+id/header_picture"
Android:text="Toolbar Subtitle"
Android:textColor="@Android:color/white" />
</RelativeLayout>
</RelativeLayout>
<FloatingActionButton
Android:id="@+id/header_fab"
Android:layout_width="wrap_content"
Android:layout_height="wrap_content"
Android:layout_gravity="bottom|right"
Android:layout_margin="10dp"
Android:src="@drawable/ic_open_in_browser"/>
</FrameLayout>
(2 Views
にまたがるファブに対して負のマージン/パディングを使用できることに注意してください)
ここからが興味深い部分です。偽のToolbar
の拡張をアニメーション化するために、ListView
onScrollListener
を実装します。
// The height of your fully expanded header view (same than in the xml layout)
int headerHeight = getResources().getDimensionPixelSize(R.dimen.header_height);
// The height of your fully collapsed header view. Actually the Toolbar height (56dp)
int minHeaderHeight = getResources().getDimensionPixelSize(R.dimen.action_bar_height);
// The left margin of the Toolbar title (according to specs, 72dp)
int toolbarTitleLeftMargin = getResources().getDimensionPixelSize(R.dimen.toolbar_left_margin);
// Added after edit
int minHeaderTranslation;
private ListView listView;
// Header views
private View headerView;
private RelativeLayout headerContainer;
private TextView headerTitle;
private TextView headerSubtitle;
private FloatingActionButton headerFab;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View rootView = inflater.inflate(R.layout.listview_fragment, container, false);
listView = rootView.findViewById(R.id.listview);
// Init the headerHeight and minHeaderTranslation values
headerHeight = getResources().getDimensionPixelSize(R.dimen.header_height);
minHeaderTranslation = -headerHeight +
getResources().getDimensionPixelOffset(R.dimen.action_bar_height);
// Inflate your header view
headerView = inflater.inflate(R.layout.header_view, listview, false);
// Retrieve the header views
headerContainer = (RelativeLayout) headerView.findViewById(R.id.header_container);
headerTitle = (TextView) headerView.findViewById(R.id.header_title);
headerSubtitle = (TextView) headerView.findViewById(R.id.header_subtitle);
headerFab = (TextView) headerView.findViewById(R.id.header_fab);;
// Add the headerView to your listView
listView.addHeaderView(headerView, null, false);
// Set the onScrollListener
listView.setOnScrollListener(this);
// ...
return rootView;
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState)
{
// Do nothing
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount)
{
Integer scrollY = getScrollY(view);
// This will collapse the header when scrolling, until its height reaches
// the toolbar height
headerView.setTranslationY(Math.max(0, scrollY + minHeaderTranslation));
// Scroll ratio (0 <= ratio <= 1).
// The ratio value is 0 when the header is completely expanded,
// 1 when it is completely collapsed
float offset = 1 - Math.max(
(float) (-minHeaderTranslation - scrollY) / -minHeaderTranslation, 0f);
// Now that we have this ratio, we only have to apply translations, scales,
// alpha, etc. to the header views
// For instance, this will move the toolbar title & subtitle on the X axis
// from its original position when the ListView will be completely scrolled
// down, to the Toolbar title position when it will be scrolled up.
headerTitle.setTranslationX(toolbarTitleLeftMargin * offset);
headerSubtitle.setTranslationX(toolbarTitleLeftMargin * offset);
// Or we can make the FAB disappear when the ListView is scrolled
headerFab.setAlpha(1 - offset);
}
// Method that allows us to get the scroll Y position of the ListView
public int getScrollY(AbsListView view)
{
View c = view.getChildAt(0);
if (c == null)
return 0;
int firstVisiblePosition = view.getFirstVisiblePosition();
int top = c.getTop();
int headerHeight = 0;
if (firstVisiblePosition >= 1)
headerHeight = this.headerHeight;
return -top + firstVisiblePosition * c.getHeight() + headerHeight;
}
このコードにはテストしなかった部分があるので、間違いをハイライトしてください。しかし、全体的には、改善できると確信しているにもかかわらず、このソリューションが機能することはわかっています。
編集2:
上記のコードにいくつかの間違いがありました(今日までテストしなかった...)ので、それを機能させるために数行を変更しました:
ヘッダービューに適用されるY変換値を変更しました:
headerView.setTranslationY(Math.max(-scrollY, minHeaderTranslation));
に:
headerView.setTranslationY(Math.max(0, scrollY + minHeaderTranslation));
前の表現はまったく機能していませんでした、ごめんなさい...
比率の計算も変更されたため、(画面の上部ではなく)ツールバーの下部から完全に展開されたヘッダーに進化します。
また、AndroidチームでChris Banesによって書かれたCollapsingTitleLayout
もチェックしてください。 https://plus.google.com/+ChrisBanes/posts/J9Fwbc15BHN
コード: https://Gist.github.com/chrisbanes/91ac8a20acfbdc410a68
設計サポートライブラリを使用 http://Android-developers.blogspot.in/2015/05/Android-design-support-library.html
これをbuild.gradleに含めます
compile 'com.Android.support:design:22.2.0'
compile 'com.Android.support:appcompat-v7:22.2.+'
リサイクル業者の見解については、これも含める
compile 'com.Android.support:recyclerview-v7:22.2.0'
<!-- AppBarLayout allows your Toolbar and other views (such as tabs provided by TabLayout)
to react to scroll events in a sibling view marked with a ScrollingViewBehavior.-->
<Android.support.design.widget.AppBarLayout
Android:id="@+id/appbar"
Android:layout_width="match_parent"
Android:layout_height="wrap_content"
Android:fitsSystemWindows="true">
<!-- specify tag app:layout_scrollFlags -->
<Android.support.v7.widget.Toolbar
Android:id="@+id/toolbar"
Android:layout_width="match_parent"
Android:layout_height="?attr/actionBarSize"
Android:background="?attr/colorPrimary"
app:layout_scrollFlags="scroll|enterAlways"/>
<!-- specify tag app:layout_scrollFlags -->
<Android.support.design.widget.TabLayout
Android:id="@+id/tabLayout"
Android:scrollbars="horizontal"
Android:layout_below="@+id/toolbar"
Android:layout_width="match_parent"
Android:layout_height="wrap_content"
Android:background="?attr/colorPrimary"
app:layout_scrollFlags="scroll|enterAlways"/>
<!-- app:layout_collapseMode="pin" will help to pin this view at top when scroll -->
<TextView
Android:layout_width="match_parent"
Android:layout_height="50dp"
Android:text="Title"
Android:gravity="center"
app:layout_collapseMode="pin" />
</Android.support.design.widget.AppBarLayout>
<!-- This will be your scrolling view.
app:layout_behavior="@string/appbar_scrolling_view_behavior" tag connects this features -->
<Android.support.v7.widget.RecyclerView
Android:id="@+id/list"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
Android:layout_width="match_parent"
Android:layout_height="match_parent">
</Android.support.v7.widget.RecyclerView>
</Android.support.design.widget.CoordinatorLayout>
アクティビティを拡張する必要がありますAppCompatActivity
public class YourActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.your_layout);
//set toolbar
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
}
}
アプリのテーマは次のようになります
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.NoActionBar">
</style>
</resources>
これは私の実装です:
collapsedHeaderHeight
とexpandedHeaderHeight
はどこかで定義されており、関数getAnimationProgress
で展開/折りたたみの進行状況を取得できます。この値に基づいてアニメーションを実行し、実際のヘッダーを表示/非表示します。
listForumPosts.setOnScrollListener(new AbsListView.OnScrollListener() {
/**
* @return [0,1], 0 means header expanded, 1 means header collapsed
*/
private float getAnimationProgress(AbsListView view, int firstVisibleItem) {
if (firstVisibleItem > 0)
return 1;
// should not exceed 1
return Math.min(
-view.getChildAt(0).getTop() / (float) (expandedHeaderHeight - collapsedHeaderHeight), 1);
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
// at render beginning, the view could be empty!
if (view.getChildCount() > 0) {
float animationProgress = getAnimationProgress(view, firstVisibleItem);
imgForumHeaderAvatar.setAlpha(1-animationProgress);
if (animationProgress == 1) {
layoutForumHeader.setVisibility(View.VISIBLE);
} else {
layoutForumHeader.setVisibility(View.GONE);
}
}
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
// do nothing
}
}