Android NestedScrollingスライド衝突問題解決(3)-プロジェクト実戦
41525 ワード
実際のニーズ
前の2つの論文ではNestedScrollに関するインタフェースと一般的な処理ロジックを理解した.本論文では,具体的な連合スライド要件を実現する.
AndroidではレイアウトにWebViewを埋め込んでWebコンテンツを表示することが多く、WebViewの内部にはインタラクティブロジック(スクロールなど)があり、外部レイアウトでもスクロールロジックを処理するとスライド衝突があり、このようなシーンは実際のプロジェクト開発でよく見られます.例えば
需要解析
このニーズに対して、
しかし、WebViewの全画面表示後、WebViewの内容をスライドできないまでスライドし続け、footerをドラッグするにはどうすればいいのでしょうか.
1つの簡単な方法は、WebViewとfooterをカスタムlayoutに配置し、プログラミングはWebViewのコンテンツスクロールとレイアウト全体のスクロール(WebViewが最後までスクロールした後にレイアウトをスクロールする)を実現することです.layoutファイルは次のとおりです.
ここで、
WebViewおよびfooterのクリックイベントに影響を与えないためには、スライド関連のイベントの処理のみをブロックする必要があります.ここでは、カスタムレイアウトの
これにより、カスタムレイアウトはスライドイベントをブロックし、各ステップのスライド距離を得ることができますが、このスライド距離をどのように処理しますか.
NestedScrollインタフェースの使用方法と特徴を振り返ると、カスタムレイアウトでスライドイベントをブロックした後、外部レイアウトと連動する必要があり、連動および制御連動を開始する側は
必要なスライドインタラクションの詳細については、スライドアップするときに、まず
ここまで解析すると,上向きにスライドする操作過程全体が明らかになった.下向きのプロセスは上向きとほぼ同じです.
処理ロジックのコードは次のとおりです.
完全な実装
このニーズに対応して、完全なAndroidエンジニアリングがGitHub上に配置されています.サンプルエンジニアリングGitHubアドレス
直接apkをダウンロードして効果を表示できます:サンプルapkダウンロード
前の2つの論文ではNestedScrollに関するインタフェースと一般的な処理ロジックを理解した.本論文では,具体的な連合スライド要件を実現する.
AndroidではレイアウトにWebViewを埋め込んでWebコンテンツを表示することが多く、WebViewの内部にはインタラクティブロジック(スクロールなど)があり、外部レイアウトでもスクロールロジックを処理するとスライド衝突があり、このようなシーンは実際のプロジェクト開発でよく見られます.例えば
AppBarLayout
を含むCoordinatorLayout
にWebViewを埋め込むなど、WebViewの底にもう1つのfooterを置いてコレクションボタンを置くなど、上へスライドするときはまずWebViewをAppBarLayout
に従ってスライドさせ、AppBarLayout
で画面をスライドさせた後、WebViewは全画面で展示され、WebViewをスライドさせ続け、WebViewは最後まで漕いだ後、WebViewとfooterを一緒に上へスライドさせ続けます.実際の効果は次の図のようになります.需要解析
このニーズに対して、
CoordinatorLayout
およびAppBarLayout
の理解によれば、WebViewをCoordinatorLayout
のサブlayoutに配置し、このlayoutのlayout_behavior
をappbar_scrolling_view_behavior
に設定することで、スライド時にAppBarLayout
の底部にWebViewを維持し、AppBarLayout
が頂部WebViewの全画面表示にスライドするまでフォローすることができる.しかし、WebViewの全画面表示後、WebViewの内容をスライドできないまでスライドし続け、footerをドラッグするにはどうすればいいのでしょうか.
1つの簡単な方法は、WebViewとfooterをカスタムlayoutに配置し、プログラミングはWebViewのコンテンツスクロールとレイアウト全体のスクロール(WebViewが最後までスクロールした後にレイアウトをスクロールする)を実現することです.layoutファイルは次のとおりです.
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/activity_root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:clipChildren="false"
android:background="#ffffff"
android:fitsSystemWindows="true">
<android.support.design.widget.CoordinatorLayout
android:id="@+id/preview_coordinator_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false"
android:fitsSystemWindows="true">
<android.support.design.widget.AppBarLayout
android:id="@+id/preview_app_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipChildren="false"
app:elevation="0dp">
<RelativeLayout
android:id="@+id/title_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/colorPrimary"
app:layout_scrollFlags="scroll">
<TextView
android:id="@+id/text_title"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:layout_alignParentTop="true"
android:gravity="center"
android:textColor="#ffffff"
android:textStyle="bold"
android:textSize="18sp"
android:text=" "
android:layout_centerHorizontal="true"/>
RelativeLayout>
android.support.design.widget.AppBarLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<com.lwons.nestedscrollexample.ScrollingContent
android:id="@+id/scrolling_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ffffff"
android:orientation="vertical">
com.lwons.nestedscrollexample.ScrollingContent>
FrameLayout>
android.support.design.widget.CoordinatorLayout>
LinearLayout>
ここで、
com.lwons.nestedscrollexample.ScrollingContent
は、LinearLayout
に基づくカスタムレイアウトである.中にはheightがMATCH_PARENT
のWebViewとheightが実際の高さのfooterが置かれています.WebViewおよびfooterのクリックイベントに影響を与えないためには、スライド関連のイベントの処理のみをブロックする必要があります.ここでは、カスタムレイアウトの
onInterceptTouchEvent
でMotionEvent
をフィルタする必要があります.次のようになります.private int mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
private float mLastY;
private boolean mIsDraging;
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
final int action = MotionEventCompat.getActionMasked(ev);
if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
mIsDraging = false;
return false;
}
switch (action) {
case MotionEvent.ACTION_MOVE: {
if (mIsDraging) {
return true;
}
final float yoff = Math.abs(mLastY - ev.getRawY());
if (yoff > mTouchSlop) {
// ,
// Start scrolling!
getParent().requestDisallowInterceptTouchEvent(true);
return true;
}
break;
}
case MotionEvent.ACTION_DOWN:
mLastY = ev.getRawY();
break;
}
return false;
}
これにより、カスタムレイアウトはスライドイベントをブロックし、各ステップのスライド距離を得ることができますが、このスライド距離をどのように処理しますか.
NestedScrollインタフェースの使用方法と特徴を振り返ると、カスタムレイアウトでスライドイベントをブロックした後、外部レイアウトと連動する必要があり、連動および制御連動を開始する側は
NestedScrollingChild
(後にNCと略称する)であるため、カスタムレイアウトでNestedScrollingChild
関連インタフェースを実現し、スライドロジックを制御する必要がある.NestedScrollingParent
(後にNPと略称する)はどのレイアウトなのか、CoordinatorLayout
のコードからNPがCoordinatorLayout
であり、AppBarLayout
のスライドを処理することがわかります.必要なスライドインタラクションの詳細については、スライドアップするときに、まず
AppBarLayout
をスライドさせ、WebViewをスライドさせる必要があります.この部分はCoordinatorLayout
が実現します.dispatchNestedPreScroll
を呼び出してCoordinatorLayout
に通知するだけでいいです.その後、AppBarLayout
が上部から滑り出した後、WebViewをスクロールし続ける必要があります.この部分は、WebViewのscrollBy
インタフェースを呼び出すだけで、自分で処理する必要があります.WebViewがスライドできない場合は、カスタムレイアウト全体をスクロールする必要があります.ここでも簡単です.カスタムレイアウトのscrollBy
インタフェースを呼び出すと、WebViewとfooter全体がスクロールされます.ここまで解析すると,上向きにスライドする操作過程全体が明らかになった.下向きのプロセスは上向きとほぼ同じです.
処理ロジックのコードは次のとおりです.
@Override
public boolean onTouchEvent(MotionEvent ev) {
boolean returnValue = false;
MotionEvent event = MotionEvent.obtain(ev);
final int action = MotionEventCompat.getActionMasked(event);
float eventY = event.getRawY();
switch (action) {
case MotionEvent.ACTION_MOVE:
if (getScrollState() == SCROLL_STAT_SCROLLING) {
stopScroll();
}
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
if (!mIsDraging) {
mIsDraging = true;
startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL, ViewCompat.TYPE_TOUCH);
}
//
int deltaY = (int) (mLastY - eventY);
mLastY = eventY;
// NP , CoordinatorLayout AppBarLayout ScrollingContent
if (dispatchNestedPreScroll(0, deltaY, mScrollConsumed, mScrollOffset, ViewCompat.TYPE_TOUCH)) {
deltaY -= mScrollConsumed[1]; // mScrollConsumed[1] CoordinatorLayout
event.offsetLocation(0, -mScrollOffset[1]);
}
mVelocityTracker.addMovement(event);
//
int scrollInternalY = 0;
if (deltaY != 0) {
scrollInternalY = scrollY(deltaY);
deltaY -= scrollInternalY;
}
// , NP (NP )
if (deltaY != 0) {
dispatchNestedScroll(0, mScrollConsumed[1]+scrollInternalY, 0, deltaY, mScrollOffset, ViewCompat.TYPE_TOUCH);
}
returnValue = true;
break;
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
returnValue = true;
mVelocityTracker.computeCurrentVelocity(1000);
// fling
onFlingY((int) -mVelocityTracker.getYVelocity());
mVelocityTracker.clear();
mIsDraging = false;
//
stopNestedScroll(ViewCompat.TYPE_TOUCH);
break;
}
return returnValue;
}
/**
*
* @param deltaY
* @return
*/
private int scrollY(int deltaY) {
int remainY = deltaY;
int consumedY = 0;
if (remainY > 0) {
//
if (mWebview != null && mWebview.canScrollUp() > 0) {
// WebView
int readerScroll = Math.min(mWebview.canScrollUp(), remainY);
mWebview.scrollBy(0, readerScroll);
remainY -= readerScroll;
consumedY += readerScroll;
}
if (remainY > 0 && getScrollY() < mFooter.getHeight()) {
//
int layoutScroll = Math.min(mFooter.getHeight() - getScrollY(), remainY);
scrollBy(0, layoutScroll);
consumedY += layoutScroll;
}
} else {
//
if (getScrollY() > 0) {
//
int layoutScroll = Math.max(-getScrollY(), remainY);
scrollBy(0, layoutScroll);
remainY -= layoutScroll;
consumedY += layoutScroll;
}
if (mWebview != null && mWebview.canScrollDown() > 0) {
// WebView
int readerScroll = Math.max(-mWebview.canScrollDown(), remainY);
mWebview.scrollBy(0, readerScroll);
consumedY += readerScroll;
}
}
return consumedY;
}
完全な実装
このニーズに対応して、完全なAndroidエンジニアリングがGitHub上に配置されています.サンプルエンジニアリングGitHubアドレス
直接apkをダウンロードして効果を表示できます:サンプルapkダウンロード