【Androidカスタムコントロール】ScrolViewなしで上下2画面スライド
前言
最近のプロジェクトの原因には、上下2画面のスライド効果が必要です.viewpagerが左右にスライドして上下にスライドするように想像できます.Srcollviewで実現しようとしたが、ボタンの競合やlistviewレイアウトの競合など、いくつかの理由でカスタムコントロールを自分で書くことにした.
構想
以前にSlidingMenuを実現したことがあるので、そのパターンを参考にして、左右スライドを上下スライドにすれば良いと考えています.実は2つの大きさの同じレイアウトで、1つはスクリーンに表示され、もう1つはスクリーンの外に隠され、スライドすると表示されます.
コード#コード#
私が引き継いだのはView Groupです.さらにonLayoutメソッドを書き直し、1つのレイアウトが画面を満たし、もう1つは画面の外に配置します.
public class MySlidingMenu extends ViewGroup {
private static final String TAG = MySlidingMenu.class.getName();
private enum Scroll_State {
Scroll_to_Open, Scroll_to_Close;
}
private Scroll_State state;
private int mMostRecentY;
private int downY;
private boolean isOpen = false;
private View menu;
private View mainView;
private Scroller mScroller;
private OnSlidingMenuListener onSlidingMenuListener;
public MySlidingMenu(Context context, View main, View menu) {
super(context);
setMainView(main);
setMenu(menu);
init(context);
}
private void init(Context context) {
mScroller = new Scroller(context);
}
@Override
protected void onLayout(boolean arg0, int l, int t, int r, int b) {
mainView.layout(l,t,r,b);
//
menu.layout(l, menu.getMeasuredHeight(), r, menu.getMeasuredHeight()*2);
}
public void setMainView(View view) {
mainView = view;
addView(mainView);
}
public void setMenu(View view) {
menu = view;
addView(menu);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mainView.measure(widthMeasureSpec, heightMeasureSpec);
menu.measure(widthMeasureSpec, heightMeasureSpec);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mMostRecentY = (int) event.getY();
downY = (int) event.getY();
break;
case MotionEvent.ACTION_MOVE:
int moveY = (int) event.getY();
int deltaY = mMostRecentY - moveY;
// Scroll
if ((!isOpen && (downY - moveY) > 0)
|| (isOpen && (downY - moveY) < 0)) {
scrollBy(0, deltaY);
}
mMostRecentY = moveY;
break;
case MotionEvent.ACTION_UP:
int upY = (int) event.getY();
int dy = upY - downY;
if (!isOpen) {
// menu
if (dy < - menu.getMeasuredHeight() / 3) {
state = Scroll_State.Scroll_to_Open;
} else {
state = Scroll_State.Scroll_to_Close;
}
} else {
// menu , menu ,
if (dy > menu.getMeasuredHeight() / 3) {
state = Scroll_State.Scroll_to_Close;
} else {
state = Scroll_State.Scroll_to_Open;
}
}
smoothScrollto();
break;
default:
break;
}
return true;
}
private void smoothScrollto() {
int scrolly = getScrollY();
switch (state) {
case Scroll_to_Close:
mScroller.startScroll(0, scrolly, 0, - scrolly, 500);
isOpen = false;
break;
case Scroll_to_Open:
mScroller.startScroll(0, scrolly, 0, menu.getMeasuredHeight()- scrolly, 500);
isOpen = true;
break;
default:
break;
}
// invalidate, computeScroll
invalidate();
}
@Override
public void computeScroll() {
// computeScrollOffset false, startScroll true
if (mScroller.computeScrollOffset()) {
scrollTo(0, mScroller.getCurrY());
postInvalidate();
}
super.computeScroll();
}
}
セグメント全体のコードは複雑ではなく、最も主要なのは座標の計算です.startScroll(int startX,int startY,int dx,int dy,int duration)という方法は座標スライドに対して計算しにくい.最初はdx、dyが正しい方向、負の方向に迷っていました.後でソースコードを見ました.
public void startScroll(int startX, int startY, int dx, int dy, int duration) {
mMode = SCROLL_MODE;
mFinished = false;
mDuration = duration;
mStartTime = AnimationUtils.currentAnimationTimeMillis();
mStartX = startX;
mStartY = startY; //
mFinalX = startX + dx;
mFinalY = startY + dy; //
mDeltaX = dx;
mDeltaY = dy;
mDurationReciprocal = 1.0f / (float) mDuration;
// This controls the viscous fluid effect (how much of it)
mViscousFluidScale = 8.0f;
// must be set to 1.0 (used in viscousFluid())
mViscousFluidNormalize = 1.0f;
mViscousFluidNormalize = 1.0f / viscousFluid(1.0f);
}
したがって、A(a 1,a 2)からB(b 1,b 2)にスライドすると、startScroll(a 1,a 2,b 1−a 1,b 2−a 2)だけでよい.主に差を求める.
使用方法
private MySlidingMenu mSlidingMenu;
......
mSlidingMenu = new MySlidingMenu(this, LayoutInflater
.from(this).inflate(R.layout.fragment1, null), LayoutInflater
.from(this).inflate(R.layout.fragment2, null));
補足
これはlistviewのスライド競合を引き起こし、列数が固定されている場合、listviewを直接書き換えるonTouchEventがfalseを返すように画面に表示されていればよい.比較的多い場合はonTouchEvent()でgetParent()を通過する.requestDisallowInterceptTouchEvent(bool)は、イベントに応答するコントロールを設定します.
一般的な考え方はlistが頭と尾をスライドするときに、対応する方向のスライドイベントを親コンポーネントに伝えることです.