如何实现类似Path、海豚浏览器、人人那样的横向侧滑导航栏?求解~

wzy820715 2012-06-07 05:39:01
最近要做一个类似Path、海豚浏览器、人人那样的横向侧滑导航栏

这个效果最早应该是在pad上用的比较多,现在手机端也逐渐开始流行起来了,

但是发现网上没有相关资料,不太清楚具体是怎么实现的,

开始觉得是用SlidingDrawer抽屉做的,但是发现抽屉没办法在收起的时候多留出一块,只能在收起时完全隐藏,同时在拉出抽屉时下面的页面是保持不动,不像海豚浏览器的收藏页面,有那种页面顶出屏幕的效果。


不知道哪位高手做过这个效果,能否分享一下或者给点思路,谢谢啦~~


...全文
657 15 打赏 收藏 转发到动态 举报
写回复
用AI写文章
15 条回复
切换为时间正序
请发表友善的回复…
发表回复
wzy820715 2012-08-21
  • 打赏
  • 举报
回复
[Quote=引用 13 楼 的回复:]

楼主实现此功能了吗?
[/Quote]
http://download.csdn.net/detail/wzy820715/4376117
wzy820715 2012-08-21
  • 打赏
  • 举报
回复
http://download.csdn.net/detail/wzy820715/4376117

最后用的这个例子,效果基本实现了,就是效果不太平滑,不过可以借鉴下这个思路,呵呵
智全 2012-08-15
  • 打赏
  • 举报
回复
楼主实现此功能了吗?
wzy820715 2012-06-16
  • 打赏
  • 举报
回复
[Quote=引用 10 楼 的回复:]

Java code

/**
* 包裹在该布局内部的子视图可以左右滑动<br>
* @author http://blog.csdn.net/Yao_GUET date: 2011-05-04
*/
public class HorizonalScrollLayout extends LinearLayout {

/**
* 通过Handler将滑屏结果通知使……
[/Quote]
有没有小demo看一看,呵呵,谢啦~~
wzy820715 2012-06-16
  • 打赏
  • 举报
回复
[Quote=引用 9 楼 的回复:]

引用 8 楼 的回复:

引用 7 楼 的回复:

那你就判断ScrollView的move事件以及方向噢,然后强制滑动到指定位置即可。


看来要重写下ScrollView了。。。先试试吧,谢啦

请问实现出来了吗?能否给个Demo
[/Quote]

网上找到了一个Demo,这个Demo很接近这个效果了,可以借鉴下它的思路
http://download.csdn.net/detail/wzy820715/4376117
jordankan 2012-06-15
  • 打赏
  • 举报
回复

/**
* 包裹在该布局内部的子视图可以左右滑动<br>
* @author http://blog.csdn.net/Yao_GUET date: 2011-05-04
*/
public class HorizonalScrollLayout extends LinearLayout {

/**
* 通过Handler将滑屏结果通知使用该滑屏控件的Activity<br>
* 必须先调用<br><code>public void setHandler(Handler handler) </code><br>来设置handler.返回的消息为:<br>
* <code>
* Message msg = new Message();<br>
msg.what = ON_SCROLL_CHANGED_MESSAGE_WHAT;<br>
msg.arg1 = whichScreen;//滑到的目标屏幕索引(0为第一个屏幕)<br>
handler.sendMessage(msg);<br>
*
* </code>
*/
public static final int ON_SCROLL_CHANGED_MESSAGE_WHAT = 2087;

private static final String TAG = "HorizonalScrollLayout";
private Scroller mScroller;
private VelocityTracker mVelocityTracker;

private int mCurScreen;
private int mDefaultScreen = 0;

private static final int TOUCH_STATE_REST = 0;
private static final int TOUCH_STATE_SCROLLING = 1;

private static final int SNAP_VELOCITY = 600;

private int mTouchState = TOUCH_STATE_REST;
private int mTouchSlop;
private float mLastMotionX;
private float mLastMotionY;

private OnSnapDestinationListener onSnapDestinationListener;


Handler handler ;


public HorizonalScrollLayout(Context context, AttributeSet attrs) {
super(context, attrs);
mScroller = new Scroller(context);

mCurScreen = mDefaultScreen;
mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
}

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// if (changed) { //如果不注释掉,里面的ListView显示出不来
int childLeft = 0;
final int childCount = getChildCount();

for (int i = 0; i < childCount; i++) {
final View childView = getChildAt(i);
if (childView.getVisibility() != View.GONE) {
final int childWidth = childView.getMeasuredWidth();
childView.layout(childLeft, 0, childLeft + childWidth, childView
.getMeasuredHeight());
childLeft += childWidth;
}
}
// }
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);

final int width = MeasureSpec.getSize(widthMeasureSpec);
final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
if (widthMode != MeasureSpec.EXACTLY) {
throw new IllegalStateException("ScrollLayout only canmCurScreen run at EXACTLY mode!");
}

final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
if (heightMode != MeasureSpec.EXACTLY) {
throw new IllegalStateException("ScrollLayout only can run at EXACTLY mode!");
}

// The children are given the same width and height as the scrollLayout
final int count = getChildCount();
for (int i = 0; i < count; i++) {
getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);
}
// Log.e(TAG, "moving to screen "+mCurScreen);
scrollTo(mCurScreen * width, 0);
}

public void setOnSnapDestinationListener(OnSnapDestinationListener onSnapDestinationListener) {
this.onSnapDestinationListener = onSnapDestinationListener;
}

/**
* According to the position of current layout scroll to the destination
* page.
*/
public void snapToDestination() {
final int screenWidth = getWidth();
final int destScreen = (getScrollX() + screenWidth / 2) / screenWidth;
snapToScreen(destScreen);
}

public void snapToScreen(int whichScreen) {
int tmp = mCurScreen;
// get the valid layout page
whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1));
if (getScrollX() != (whichScreen * getWidth())) {
final int delta = whichScreen * getWidth() - getScrollX();
mScroller.startScroll(getScrollX(), 0, delta, 0, Math.abs(delta));
mCurScreen = whichScreen;
invalidate(); // Redraw the layout
}
if(tmp == whichScreen){
return;
}
if(handler!= null){
Message msg = new Message();
msg.what = ON_SCROLL_CHANGED_MESSAGE_WHAT;
msg.arg1 = whichScreen;
handler.sendMessage(msg);
}
}

public void setToScreen(int whichScreen) {
whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1));
mCurScreen = whichScreen;
scrollTo(whichScreen * getWidth(), 0);
invalidate();
}

public int getCurScreen() {
return mCurScreen;
}

@Override
public void computeScroll() {
if (mScroller.computeScrollOffset()) {
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
postInvalidate();
}
}

@Override
public boolean onTouchEvent(MotionEvent event) {
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(event);

final int action = event.getAction();
final float x = event.getX();
final float y = event.getY();

switch (action) {
case MotionEvent.ACTION_DOWN:
// Log.e(TAG, "event down!");
if (!mScroller.isFinished()) {
mScroller.abortAnimation();
}
mLastMotionX = x;
break;

case MotionEvent.ACTION_MOVE:
int deltaX = (int) (mLastMotionX - x);
mLastMotionX = x;

scrollBy(deltaX, 0);
break;

case MotionEvent.ACTION_UP:
// Log.e(TAG, "event : up");
// if (mTouchState == TOUCH_STATE_SCROLLING) {
final VelocityTracker velocityTracker = mVelocityTracker;
velocityTracker.computeCurrentVelocity(1000);
int velocityX = (int) velocityTracker.getXVelocity();

// Log.e(TAG, "velocityX:" + velocityX);

if (velocityX > SNAP_VELOCITY && mCurScreen > 0) {
// Fling enough to move left
// Log.e(TAG, "snap left");
if(onSnapDestinationListener == null || onSnapDestinationListener.isAbleToSnap())
snapToScreen(mCurScreen - 1);
else
snapToScreen(mCurScreen);
} else if (velocityX < -SNAP_VELOCITY && mCurScreen < getChildCount() - 1) {
// Fling enough to move right
// Log.e(TAG, "snap right");
if(onSnapDestinationListener == null || onSnapDestinationListener.isAbleToSnap())
snapToScreen(mCurScreen + 1);
else
snapToScreen(mCurScreen);
} else {
snapToDestination();
}

if (mVelocityTracker != null) {
mVelocityTracker.recycle();
mVelocityTracker = null;
}
// }
mTouchState = TOUCH_STATE_REST;
break;
case MotionEvent.ACTION_CANCEL:
mTouchState = TOUCH_STATE_REST;
break;
}

return true;
}

public void setHandler(Handler handler) {
this.handler = handler;
}


@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
// Log.e(TAG, "onInterceptTouchEvent-slop:" + mTouchSlop);

final int action = ev.getAction();
if ((action == MotionEvent.ACTION_MOVE) && (mTouchState != TOUCH_STATE_REST)) {
return true;
}

final float x = ev.getX();
final float y = ev.getY();

switch (action) {
case MotionEvent.ACTION_MOVE:

/*
* 2011.12.29
* 如果内部的子视图有ScrollView,当用户在ScrollView上下滑动的时候,判断如果Y轴变化值大于X轴变化的1.2倍,就不要消费这个事件了
* 否则会导致用户本来是想上下滑动ScrollView 结果这个滑动事件被本视图吸收了,变为左右滑动
*/
int deltaX = (int) (mLastMotionX - x);
int deltaY = (int) (mLastMotionY - y);
if(Math.abs(deltaY) > Math.abs(deltaX * 1.2)){
return false;
}
/*
* =========================================
*/

final int xDiff = (int) Math.abs(mLastMotionX - x);
if (xDiff > mTouchSlop) {
mTouchState = TOUCH_STATE_SCROLLING;
}
break;
case MotionEvent.ACTION_DOWN:
mLastMotionX = x;
mLastMotionY = y;
mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST : TOUCH_STATE_SCROLLING;
break;

case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
mTouchState = TOUCH_STATE_REST;
break;
}

return mTouchState != TOUCH_STATE_REST;
}

public interface OnSnapDestinationListener{

/**
* 当滑到目标屏幕的时间发生之前调用,如果返回false;则不继续
* @return
* @author : wang.jun<br>
* @time : Mar 12, 2012 4:17:24 PM<br>
*/
public boolean isAbleToSnap();

}

}
wallkaka 2012-06-15
  • 打赏
  • 举报
回复
[Quote=引用 8 楼 的回复:]

引用 7 楼 的回复:

那你就判断ScrollView的move事件以及方向噢,然后强制滑动到指定位置即可。


看来要重写下ScrollView了。。。先试试吧,谢啦
[/Quote]
请问实现出来了吗?能否给个Demo
wzy820715 2012-06-08
  • 打赏
  • 举报
回复
[Quote=引用 7 楼 的回复:]

那你就判断ScrollView的move事件以及方向噢,然后强制滑动到指定位置即可。
[/Quote]

看来要重写下ScrollView了。。。先试试吧,谢啦
AMinfo 2012-06-08
  • 打赏
  • 举报
回复
那你就判断ScrollView的move事件以及方向噢,然后强制滑动到指定位置即可。
wzy820715 2012-06-08
  • 打赏
  • 举报
回复
[Quote=引用 5 楼 的回复:]

可以考虑这样做比较快
[/Quote]

如果这样的话就相当于要做一个比手机屏幕大的多的xml布局吧,然后用ScrollView滑动布局,来显示在屏幕上?这样的话用户可以随意滑动到布局的任意位置,不能实现 只允许滑动到上面图片里的那3个指定位置的效果。。。
AMinfo 2012-06-08
  • 打赏
  • 举报
回复
可以考虑这样做比较快
wzy820715 2012-06-07
  • 打赏
  • 举报
回复
[Quote=引用 3 楼 的回复:]

用ScrollView + FrameLayout 就可以实现了。

例如你上图的是3个屏,FrameLayout里面分别放3个屏的Layout,
[/Quote]
你的意思是把三个屏的Layout用FrameLayout做局部叠加的效果,然后再把FrameLayout外面套一个ScrollView?
AMinfo 2012-06-07
  • 打赏
  • 举报
回复
用ScrollView + FrameLayout 就可以实现了。

例如你上图的是3个屏,FrameLayout里面分别放3个屏的Layout,
wzy820715 2012-06-07
  • 打赏
  • 举报
回复
坐等高手
wzy820715 2012-06-07
  • 打赏
  • 举报
回复
自己先顶下吧~~~

80,493

社区成员

发帖
与我相关
我的任务
社区描述
移动平台 Android
androidandroid-studioandroidx 技术论坛(原bbs)
社区管理员
  • Android
  • yechaoa
  • 失落夏天
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧