アプリにiOSのようなバウンスオーバースクロール効果を実装したい。
私はこれに遭遇しました link これはカスタムScrollView
の作成を提案します。しかし、問題は、高速で上下にスクロールしているときは正常に機能しているが、画面の下部または上部を引っ張るとすぐに動かなくなって、効果が機能しなくなることです。
私が達成したい種類のアニメーションの例として、これを見ることができます:
これは私が現在持っているコードです:
public class ObservableScrollView extends ScrollView
{
private static final int MAX_Y_OVERSCROLL_DISTANCE = 150;
private Context mContext;
private int mMaxYOverscrollDistance;
public ObservableScrollView(Context context)
{
super(context);
mContext = context;
initBounceScrollView();
}
public ObservableScrollView(Context context, AttributeSet attrs)
{
super(context, attrs);
mContext = context;
initBounceScrollView();
}
public ObservableScrollView(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
mContext = context;
initBounceScrollView();
}
private void initBounceScrollView()
{
//get the density of the screen and do some maths with it on the max overscroll distance
//variable so that you get similar behaviors no matter what the screen size
final DisplayMetrics metrics = mContext.getResources().getDisplayMetrics();
final float density = metrics.density;
mMaxYOverscrollDistance = (int) (density * MAX_Y_OVERSCROLL_DISTANCE);
}
@Override
protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent)
{
//This is where the magic happens, we have replaced the incoming maxOverScrollY with our own custom variable mMaxYOverscrollDistance;
return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, mMaxYOverscrollDistance, isTouchEvent);
}
}
_CoordinatorLayout.Behavior
_に基づく簡単なソリューションをすぐにまとめました。完璧ではありません。少し時間をかけて微調整することもできますが、悪くはありません。とにかく、結果は次のようになります。
回答を始める前のちょっとした補足として、通常のNestedScrollView
ではなく、サポートライブラリのScrollView
を使用することを強くお勧めします。それらはまったく同じですが、NestedScrollView
は、より低いAPIレベルで正しいネストされたスクロール動作を実装します。
とにかく、私の答えから始めましょう:私が思いついた解決策は、ScrollView
、ListView
、またはRecyclerView
などのスクロール可能なコンテナで機能し、あなたはする必要はありませんViews
をサブクラス化して実装します。
まだ使用していない場合は、まずプロジェクトにGoogleのデザインサポートライブラリを追加する必要があります。
_compile 'com.Android.support:design:25.0.1'
_
APIレベル25(ちなみにこれを行う必要があります)を対象としていない場合は、APIレベルの最新バージョンを含める必要があります(たとえば、APIレベル24の場合は_compile 'com.Android.support:design:24.2.0'
_)。
使用しているスクロール可能なコンテナは、レイアウトのCoordinatorLayout
でラップする必要があります。私の例では、NestedScrollView
を使用しています。
_<?xml version="1.0" encoding="utf-8"?>
<Android.support.design.widget.CoordinatorLayout
xmlns:Android="http://schemas.Android.com/apk/res/Android"
Android:layout_width="match_parent"
Android:layout_height="match_parent">
<Android.support.v4.widget.NestedScrollView
Android:layout_width="match_parent"
Android:layout_height="match_parent">
<!-- content -->
</Android.support.v4.widget.NestedScrollView>
</Android.support.design.widget.CoordinatorLayout>
_
CoordinatorLayout
を使用すると、Behavior
を直接の子ビューに割り当てることができます。この場合、オーバースクロールバウンス効果を実装するBehavior
にNestedScrollView
を割り当てます。
Behavior
のコードを見てみましょう:
_public class OverScrollBounceBehavior extends CoordinatorLayout.Behavior<View> {
private int mOverScrollY;
public OverScrollBounceBehavior() {
}
public OverScrollBounceBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, View child, View directTargetChild, View target, int nestedScrollAxes) {
mOverScrollY = 0;
return true;
}
@Override
public void onNestedScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
if (dyUnconsumed == 0) {
return;
}
mOverScrollY -= dyUnconsumed;
final ViewGroup group = (ViewGroup) target;
final int count = group.getChildCount();
for (int i = 0; i < count; i++) {
final View view = group.getChildAt(i);
view.setTranslationY(mOverScrollY);
}
}
@Override
public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, View child, View target) {
final ViewGroup group = (ViewGroup) target;
final int count = group.getChildCount();
for (int i = 0; i < count; i++) {
final View view = group.getChildAt(i);
ViewCompat.animate(view).translationY(0).start();
}
}
}
_
Behavior
が何であり、どのように機能するかを説明することは、この回答の範囲を超えているため、上記のコードが何をするのかを簡単に説明します。 Behavior
は、CoordinatorLayout
の直接の子で発生するすべてのスクロールイベントをインターセプトします。 onStartNestedScroll()
メソッドでは、スクロールイベントに関心があるため、true
を返します。 onNestedScroll()
では、垂直スクロールのどの程度がスクロールコンテナーによって消費されなかったか(つまりオーバースクロール)を示すdyUnconsumed
パラメーターを確認し、スクロールコンテナーの子を変換します。その量で。デルタ値を取得しているだけなので、mOverscrollY
変数にすべてのデルタ値を合計する必要があります。 onStopNestedScroll()
は、スクロールイベントが停止したときに呼び出されます。これは、スクロールコンテナーのすべての子を元の位置にアニメーション化するときです。
Behavior
をNestedScrollView
に割り当てるには、_layout_behavior
_ xml属性を使用して、使用するBehavior
の完全なクラス名を渡す必要があります。私の例では、上記のクラスはパッケージ_com.github.wrdlbrnft.testapp
_にあるので、値として_com.github.wrdlbrnft.testapp.OverScrollBounceBehavior
_を設定する必要があります。 _layout_behavior
_はCoordinatorLayout
のカスタム属性であるため、正しい名前空間をプレフィックスとして付ける必要があります。
_<?xml version="1.0" encoding="utf-8"?>
<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.support.v4.widget.NestedScrollView
Android:layout_width="match_parent"
Android:layout_height="match_parent"
app:layout_behavior="com.github.wrdlbrnft.testapp.OverScrollBounceBehavior">
<!-- content -->
</Android.support.v4.widget.NestedScrollView>
</Android.support.design.widget.CoordinatorLayout>
_
CoordinatorLayout
に追加した名前空間と、NestedScrollView
に追加した_app:layout_behavior
_属性に注意してください。
そして、それはあなたがしなければならないすべてです!この回答は意図したよりも長いことが判明しましたが、CoordinatorLayout
およびBehaviors
に関する基本事項の一部はスキップしました。そのため、これらに慣れていない場合や、その他の質問がある場合は、遠慮なく質問してください。
Xaver Kapellerのおかげで、kotlinとandroidx
コーディネーターの依存関係を追加する
implementation "androidx.coordinatorlayout:coordinatorlayout:1.1.0"
CoordinatorLayout.Behaviorを拡張する新しいクラスを作成します
import Android.content.Context
import Android.util.AttributeSet
import Android.view.View
import Android.view.ViewGroup
import Android.view.animation.AccelerateDecelerateInterpolator
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.view.ViewCompat
class OverScrollBehavior(context: Context, attributeSet: AttributeSet)
: CoordinatorLayout.Behavior<View>() {
companion object {
private const val OVER_SCROLL_AREA = 4
}
private var overScrollY = 0
override fun onStartNestedScroll(
coordinatorLayout: CoordinatorLayout,
child: View,
directTargetChild: View,
target: View,
axes: Int,
type: Int
): Boolean {
overScrollY = 0
return true
}
override fun onNestedScroll(
coordinatorLayout: CoordinatorLayout,
child: View,
target: View,
dxConsumed: Int,
dyConsumed: Int,
dxUnconsumed: Int,
dyUnconsumed: Int,
type: Int,
consumed: IntArray
) {
if (dyUnconsumed == 0) {
return
}
overScrollY -= (dyUnconsumed/OVER_SCROLL_AREA)
val group = target as ViewGroup
val count = group.childCount
for (i in 0 until count) {
val view = group.getChildAt(i)
view.translationY = overScrollY.toFloat()
}
}
override fun onStopNestedScroll(
coordinatorLayout: CoordinatorLayout,
child: View,
target: View,
type: Int
) {
// Smooth animate to 0 when the user stops scrolling
moveToDefPosition(target)
}
override fun onNestedPreFling(
coordinatorLayout: CoordinatorLayout,
child: View,
target: View,
velocityX: Float,
velocityY: Float
): Boolean {
// Scroll view by inertia when current position equals to 0
if (overScrollY == 0) {
return false
}
// Smooth animate to 0 when user fling view
moveToDefPosition(target)
return true
}
private fun moveToDefPosition(target: View) {
val group = target as ViewGroup
val count = group.childCount
for (i in 0 until count) {
val view = group.getChildAt(i)
ViewCompat.animate(view)
.translationY(0f)
.setInterpolator(AccelerateDecelerateInterpolator())
.start()
}
}
}
CoordinatorLayoutとNestedScrollViewを使用してXMLファイルを作成する
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
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:layout_width="match_parent"
Android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.core.widget.NestedScrollView
Android:id="@+id/scrollView"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
app:layout_behavior=".OverScrollBehavior">
<TextView
Android:layout_width="match_parent"
Android:layout_height="match_parent"
Android:textAlignment="center"
Android:padding="10dp"
Android:text="@string/Lorem"/>
</androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
そして、追加することを忘れないでください
app:layout_behavior=".OverScrollBehavior" // Or your file name
nestedScrollView XMLマークアップへのフィールド