先看效果图
实现思路:1. 重写 ScrollView ,添加滑动监听,2. 根据控件的坐标实现精确滑动。注释写的比较详细,直接上代码
/** * Created by John on 2017/10/31. * * 重写带有滑动监听的 ScrollView */ public class MyScrollView extends ScrollView { private ScrollViewListener scrollViewListener = null; private OnScrollViewTouchDown onScrollViewTouchDown = null; private int handlerWhatId = 65984; private int timeInterval = 20; private int lastY = 0; @SuppressLint("HandlerLeak") Handler handler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); if (msg.what == handlerWhatId) { if (lastY == getScrollY()) { if (scrollViewListener != null) { scrollViewListener.onScrollStop(true); } } else { if (scrollViewListener != null) { scrollViewListener.onScrollStop(false); } handler.sendMessageDelayed(handler.obtainMessage(handlerWhatId, this), timeInterval); lastY = getScrollY(); } } } }; public MyScrollView(Context context) { super(context); } public MyScrollView(Context context, AttributeSet attrs) { super(context, attrs); } public MyScrollView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } /** * 滚动监听 */ public void setScrollViewListener(ScrollViewListener scrollViewListener) { this.scrollViewListener = scrollViewListener; } /** * 自定义 ScrollView 滑动监听 */ public interface ScrollViewListener { /** * 滑动监听 * * @param scrollView ScrollView控件 * @param x x轴坐标 * @param y y轴坐标 * @param oldx 上一个x轴坐标 * @param oldy 上一个y轴坐标 */ void onScrollChanged(ScrollView scrollView, int x, int y, int oldx, int oldy); /** * 是否滑动停止 * * @param isScrollStop true:滑动停止;false:未滑动停止 */ void onScrollStop(boolean isScrollStop); } /** * 按下监听 */ public void setOnScrollViewTouchDown(OnScrollViewTouchDown onScrollViewTouchDown) { this.onScrollViewTouchDown = onScrollViewTouchDown; } /** * 自定义 ScrollView 触摸监听 */ public interface OnScrollViewTouchDown { /** * 按下监听 * * @param isTouchDown 是否按下 */ void onTouchDown(boolean isTouchDown); } @Override protected void onScrollChanged(int x, int y, int oldx, int oldy) { super.onScrollChanged(x, y, oldx, oldy); if (scrollViewListener != null) { scrollViewListener.onScrollChanged(this, x, y, oldx, oldy); } } @Override public boolean onTouchEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_UP) { handler.sendMessageDelayed(handler.obtainMessage(handlerWhatId, this), timeInterval); if (onScrollViewTouchDown != null) { onScrollViewTouchDown.onTouchDown(true); } } return super.onTouchEvent(ev); } }
/** * Created by John on 2017/10/31. * <p> * 上下滑动标题联动、标题栏固定到顶部 效果 */ public class MainActivity extends Activity implements View.OnClickListener { // 滑动 scrollview private MyScrollView my_scrollview; // 固定标题和滑动标题 LinearLayout private LinearLayout ll_title_in_scrollview, ll_title_top; // 固定标题 private TextView tv_title1, tv_title2, tv_title3; // ScrollView 中的标题 private TextView tv_title_in_scrollview1, tv_title_in_scrollview2, tv_title_in_scrollview3; // top界面、3个body界面 private LinearLayout ll_top_view, ll_body1, ll_body2, ll_body3; // 动画图片 private ImageView imgv_cursor; // 动画图片偏移量 private int offset = 0; // 上一个界面 id private int lastTabIndex = 0; /** * 用于在同一个内容模块内滑动,锁定导航标签,防止重复刷新标签 * true: 锁定 * false ; 没有锁定 */ private boolean content2NavigateFlagInnerLock = false; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); initImageView(); addClickListener(); addScrollListener(); } /** * 初始化界面控件 */ private void initView() { ll_title_in_scrollview = findViewById(R.id.ll_title_in_scrollview); ll_title_top = findViewById(R.id.ll_title_top); tv_title1 = findViewById(R.id.tv_title1); tv_title2 = findViewById(R.id.tv_title2); tv_title3 = findViewById(R.id.tv_title3); tv_title_in_scrollview1 = findViewById(R.id.tv_title_in_scrollview1); tv_title_in_scrollview2 = findViewById(R.id.tv_title_in_scrollview2); tv_title_in_scrollview3 = findViewById(R.id.tv_title_in_scrollview3); imgv_cursor = findViewById(R.id.imgv_cursor); my_scrollview = findViewById(R.id.my_scrollview); ll_top_view = findViewById(R.id.ll_top_view); ll_body1 = findViewById(R.id.ll_body1); ll_body2 = findViewById(R.id.ll_body2); ll_body3 = findViewById(R.id.ll_body3); } /** * 初始化滑动条 */ private void initImageView() { DisplayMetrics dm = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(dm); int screenW = dm.widthPixels;// 获取分辨率宽度 offset = screenW / 3;// 计算偏移量 LinearLayout.LayoutParams ll = new LinearLayout.LayoutParams(offset, 4); imgv_cursor.setLayoutParams(ll); Matrix matrix = new Matrix(); matrix.postTranslate(offset, 0); imgv_cursor.setImageMatrix(matrix);// 设置动画初始位置 } /** * 添加标题点击监听 */ private void addClickListener() { tv_title1.setOnClickListener(this); tv_title2.setOnClickListener(this); tv_title3.setOnClickListener(this); tv_title_in_scrollview1.setOnClickListener(this); tv_title_in_scrollview2.setOnClickListener(this); tv_title_in_scrollview3.setOnClickListener(this); } /** * 添加滑动监听 */ private void addScrollListener() { my_scrollview.setScrollViewListener(new MyScrollView.ScrollViewListener() { @Override public void onScrollChanged(ScrollView scrollView, int x, int y, int oldx, int oldy) { scrollRefreshNavigationTag(scrollView); } @Override public void onScrollStop(boolean isScrollStop) { } }); } /** * 内容区域滑动刷新导航标签 * * @param scrollView 内容模块容器 */ private void scrollRefreshNavigationTag(ScrollView scrollView) { if (scrollView == null) { return; } // 获得ScrollView滑动距离 int scrollY = scrollView.getScrollY(); // 确定头部标题显示或隐藏 if (scrollY >= ll_title_in_scrollview.getTop()) { ll_title_top.setVisibility(View.VISIBLE); } else { ll_title_top.setVisibility(View.GONE); } // 确定ScrollView当前展示的顶部内容属于哪个内容模块 if (scrollY >= ll_body3.getTop() - ll_title_in_scrollview.getHeight()) { refreshContent2NavigationFlag(2); } else if (scrollY >= ll_body2.getTop() - ll_title_in_scrollview.getHeight()) { refreshContent2NavigationFlag(1); } else if (scrollY >= ll_body1.getTop() - ll_title_in_scrollview.getHeight()) { refreshContent2NavigationFlag(0); } } /** * 刷新标签 * * @param currentTagIndex 当前模块位置 */ private void refreshContent2NavigationFlag(int currentTagIndex) { // 上一个位置与当前位置不一致是,解锁内部锁,是导航可以发生变化 if (lastTabIndex != currentTagIndex) { content2NavigateFlagInnerLock = false; } if (!content2NavigateFlagInnerLock) { // 锁定内部锁 content2NavigateFlagInnerLock = true; // 滑动到指定位置 splippingToTab(currentTagIndex); } lastTabIndex = currentTagIndex; } /** * 滑动到指定的位置 * * @param tabIndex */ private void splippingToTab(int tabIndex) { Animation animation = new TranslateAnimation(offset * lastTabIndex, offset * tabIndex, 0, 0); animation.setFillAfter(true);// True:图片停在动画结束位置 animation.setDuration(200); imgv_cursor.startAnimation(animation); restoreStyle(); if (tabIndex == 0) { setStyle(tv_title1); } else if (tabIndex == 1) { setStyle(tv_title2); } else if (tabIndex == 2) { setStyle(tv_title3); } } /** * 还原字体默认颜色 */ private void restoreStyle() { tv_title1.setTextColor(getResources().getColor(R.color.new_grey1)); tv_title2.setTextColor(getResources().getColor(R.color.new_grey1)); tv_title3.setTextColor(getResources().getColor(R.color.new_grey1)); } /** * 设置选中字体颜色 */ private void setStyle(TextView txt) { txt.setTextColor(getResources().getColor(R.color.new_purple)); } /** * 点击事件 */ @Override public void onClick(View view) { switch (view.getId()) { case R.id.tv_title1: // 顶部标题 my_scrollview.smoothScrollTo(0, ll_body1.getTop() - ll_title_in_scrollview.getHeight()); break; case R.id.tv_title2: my_scrollview.smoothScrollTo(0, ll_body2.getTop() - ll_title_in_scrollview.getHeight()); break; case R.id.tv_title3: my_scrollview.smoothScrollTo(0, ll_body3.getTop() - ll_title_in_scrollview.getHeight()); break; case R.id.tv_title_in_scrollview1: // 滑动标题 my_scrollview.smoothScrollTo(0, ll_body1.getTop() - ll_title_in_scrollview.getHeight()); break; case R.id.tv_title_in_scrollview2: my_scrollview.smoothScrollTo(0, ll_body2.getTop() - ll_title_in_scrollview.getHeight()); break; case R.id.tv_title_in_scrollview3: my_scrollview.smoothScrollTo(0, ll_body3.getTop() - ll_title_in_scrollview.getHeight()); break; } } }
本人抛砖引玉,希望大家能有更好的实现思路
demo 下载地址: http://download.csdn.net/download/qiushuiduren/10172007