サイドスライドメニューのカスタマイズ
7636 ワード
同期はhttp://avenwu.net/customlayout/2014/12/16/sliding-menu/に発表されました
Fork on github https://github.com/avenwu/support図なし真相なし、完全なコード
構想
2年前からサイドスライドメニューがヒットし、今ではよく見られるインタラクティブなレイアウトとなっていますが、開発者としての私たちは実は選択が非常に多く、オープンソースも公式もあります. Android support v 4拡張パッケージのDrawerLayout Android support v 4拡張パッケージのSlidingPaneLayout 比較的有名な三方オープンソースライブラリSlidingMenu その他...
これらのコントロールの下部で使用される技術は実際には類似しており、これらのホイールがどのように作られているかをより深く理解するために、本稿では簡単なサイドスライドコントロールの実現に着手する.
せっけい構想
まず、最も基礎的なメニューを実現するには、それらの技術点を解決する必要があることを一緒に分析することができます.インタフェースはメニュー領域とコンテンツ領域に分かれ、スライド表示、非表示メニュー を介している.ジェスチャー配信処理 スライド停止後、位置に応じて自動的に表示、非表示操作 が完了する.
実装の詳細
前述したいくつかの技術点に基づいて,今から逐一処理を開始する.
区画
ここでは、ベースクラスとしてFrameLayoutを選択し、メニュービューとコンテンツビューの階層関数を処理しないようにします.
ジェスチャー配布
ジェスチャー処理にはtouch eventの配布と消費が含まれており、onInterceptTouchEventでは現在メニュースライド状態にあるかどうかを簡単に判断することができ、そうであれば後続のジェスチャーをブロックすることができる.
ジェスチャー処理
メニューのスライド位置を処理する必要があります.スライド中にMotionEvent.ACTION_MOVEは絶えずトリガーされるので、ここでメニューviewの位置を変えることができ、ここではScrollerを利用して位置の変化を担当する.MotionEvent.ACTION_UPではジェスチャー持ち上げ時のメニュー位置状態を判断し,スライド位置がメニュー幅の1/2に達していれば,メニューを開き続ける必要があると考え,逆に閉じる.
ビュー位置の初期化
ジェスチャーの問題に加えて、コンテナ内でビューを初期化する必要があります.ここではonLayoutを複写する必要があります.2つのサブview(メニュー領域、コンテンツ領域)しかないため、デフォルトのメニューは閉じています.
スムーズスクロール
前述したように、メニューの位置を変更するには、主にスムーズなスクロールの問題を考慮したScrollerが使用されています.
小結
これで簡単なサイドスライドメニューをカスタマイズする主な技術点が解決し、その他の詳細は完全なコードを見ることができます.
Fork on github https://github.com/avenwu/support図なし真相なし、完全なコード
構想
2年前からサイドスライドメニューがヒットし、今ではよく見られるインタラクティブなレイアウトとなっていますが、開発者としての私たちは実は選択が非常に多く、オープンソースも公式もあります.
これらのコントロールの下部で使用される技術は実際には類似しており、これらのホイールがどのように作られているかをより深く理解するために、本稿では簡単なサイドスライドコントロールの実現に着手する.
せっけい構想
まず、最も基礎的なメニューを実現するには、それらの技術点を解決する必要があることを一緒に分析することができます.
実装の詳細
前述したいくつかの技術点に基づいて,今から逐一処理を開始する.
区画
ここでは、ベースクラスとしてFrameLayoutを選択し、メニュービューとコンテンツビューの階層関数を処理しないようにします.
main = new FrameLayout(getContext());
main.setId(R.id.main);
main.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
main.setBackgroundColor(getResources().getColor(android.R.color.holo_blue_bright));
addView(main);
left = new FrameLayout(getContext());
left.setId(R.id.menu);
left.setLayoutParams(new FrameLayout.LayoutParams(MENU_WIDTH, ViewGroup.LayoutParams.MATCH_PARENT));
addView(left);
ジェスチャー配布
ジェスチャー処理にはtouch eventの配布と消費が含まれており、onInterceptTouchEventでは現在メニュースライド状態にあるかどうかを簡単に判断することができ、そうであれば後続のジェスチャーをブロックすることができる.
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (!mSlidable) return false;
final int action = ev.getAction();
if (action != MotionEvent.ACTION_DOWN && isSliding) return true;
switch (action) {
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
return false;
case MotionEvent.ACTION_MOVE:
if (Math.abs(ev.getX() - mSrcX) > touchSlop) {
mSrcX = ev.getX();
isSliding = true;
}
break;
case MotionEvent.ACTION_DOWN:
isSliding = false;
mSrcX = ev.getX();
break;
}
return isSliding;
}
ジェスチャー処理
メニューのスライド位置を処理する必要があります.スライド中にMotionEvent.ACTION_MOVEは絶えずトリガーされるので、ここでメニューviewの位置を変えることができ、ここではScrollerを利用して位置の変化を担当する.MotionEvent.ACTION_UPではジェスチャー持ち上げ時のメニュー位置状態を判断し,スライド位置がメニュー幅の1/2に達していれば,メニューを開き続ける必要があると考え,逆に閉じる.
@Override
public boolean onTouchEvent(MotionEvent event) {
d("UIView", "event:" + event.toString());
final int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
mSrcX = event.getX();
break;
case MotionEvent.ACTION_MOVE:
int oldx = left.getScrollX();
float dx = mSrcX - event.getX();
if (dx != 0) {
left.setVisibility(VISIBLE);
float x = oldx + dx;
d("onTouchEvent", "move, oldx=" + oldx + ", dx=" + dx + ", left=" +
left.getLeft() + ", right=" + left.getRight() + ", getX=" + event.getX() + ", mSrcX=" + mSrcX);
d("onTouchEvent", "before x=" + x);
if (MENU_WIDTH < x) {
x = MENU_WIDTH;
}
if (0 > x) {
x = 0;
}
d("onTouchEvent", "after x=" + x);
left.scrollTo((int) x, 0);
mSrcX = event.getX();
}
break;
case MotionEvent.ACTION_UP:
int currentX = left.getScrollX();
if (currentX + mSrcX - event.getX() >= MENU_WIDTH / 2.0) {
int duration = (int) (Math.abs(MENU_WIDTH - currentX + 0.5f) / MENU_WIDTH * 1000);
scroller.startScroll(currentX, 0, MENU_WIDTH - currentX, 0, duration);
invalidate();
} else {
int duration = (int) (Math.abs(currentX + 0.5f) / MENU_WIDTH * 1000);
scroller.startScroll(currentX, 0, 0 - currentX, 0, duration);
invalidate();
}
break;
case MotionEvent.ACTION_CANCEL:
break;
}
return true;
}
ビュー位置の初期化
ジェスチャーの問題に加えて、コンテナ内でビューを初期化する必要があります.ここではonLayoutを複写する必要があります.2つのサブview(メニュー領域、コンテンツ領域)しかないため、デフォルトのメニューは閉じています.
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
int count = getChildCount();
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
if (child.getVisibility() == GONE) continue;
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
if (child.getId() == R.id.menu) {
final int childWidth = child.getMeasuredWidth();
final int childHeight = child.getMeasuredHeight();
int childLeft = 0;
child.layout(childLeft, lp.topMargin, childLeft + childWidth, lp.topMargin + childHeight);
child.scrollTo(MENU_WIDTH, 0);
} else if (child.getId() == R.id.main) {
child.layout(0, 0, child.getMeasuredWidth(), child.getMeasuredHeight());
}
}
}
スムーズスクロール
前述したように、メニューの位置を変更するには、主にスムーズなスクロールの問題を考慮したScrollerが使用されています.
@Override
public void computeScroll() {
d("computeScroll", "computeScroll");
if (scroller.computeScrollOffset()) {
int oldx = left.getScrollX();
int x = scroller.getCurrX();
d("computeScroll", "try scroll, oldx=" + oldx + ", x=" + x);
if (oldx != x) {
//this can only effect on the content view inside of left
left.scrollTo(x, 0);
left.invalidate();
}
invalidate();
} else {
scroller.abortAnimation();
}
}
小結
これで簡単なサイドスライドメニューをカスタマイズする主な技術点が解決し、その他の詳細は完全なコードを見ることができます.