ScrollView作为一个支持可以垂直滑动的容器控件,如果不对它进行拓展,而是简单使用的话那确实比较简单。但是,它虽然简单,拓展性能却很强。比如可以实现阻尼效果,可以监听滑动情况,可以解决滑动冲突问题等。那下面就开始逐一讲解一下。
有时候我们需要监听ScroView的滑动情况,比如滑动了多少距离,是否滑到布局的顶部或者底部。可惜的是SDK并没有相应的方法,不过倒是提供了一个
protected void onScrollChanged(int x, int y, int oldx, int oldy)方法,显然这个方法是不能被外界调用的,因此就需要把它暴露出去,方便使用。解决方式就是写一个接口,
package com.example.demo1; public interface ScrollViewListener { void onScrollChanged(ObservableScrollView scrollView, int x, int y, int oldx, int oldy); }
然后重写ScrollView类,给它提供上面写的回调接口。
package com.example.demo1; import android.content.Context; import android.util.AttributeSet; import android.widget.ScrollView; public class ObservableScrollView extends ScrollView { private ScrollViewListener scrollViewListener = null; public ObservableScrollView(Context context) { super(context); } public ObservableScrollView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public ObservableScrollView(Context context, AttributeSet attrs) { super(context, attrs); } public void setScrollViewListener(ScrollViewListener scrollViewListener) { this.scrollViewListener = scrollViewListener; } @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); } } }
注意在xml布局的时候,不要写错了包。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context=".MainActivity" > <com.example.demo1.ObservableScrollView android:id="@+id/view1" android:layout_width="wrap_content" android:layout_height="match_parent" > <LinearLayout android:layout_width="wrap_content" android:layout_height="match_parent" android:orientation="vertical" > <TextView android:layout_width="100dp" android:layout_height="100dp" android:text="试试1" /> <TextView android:layout_width="100dp" android:layout_height="100dp" android:text="试试2" /> <TextView android:layout_width="100dp" android:layout_height="100dp" android:text="试试3" /> <TextView android:layout_width="100dp" android:layout_height="100dp" android:text="试试4" /> <TextView android:layout_width="100dp" android:layout_height="100dp" android:text="试试5" /> <TextView android:layout_width="100dp" android:layout_height="100dp" android:text="试试6" /> </LinearLayout> </com.example.demo1.ObservableScrollView> <com.example.demo1.ObservableScrollView android:id="@+id/view2" android:layout_width="wrap_content" android:layout_height="match_parent" > <LinearLayout android:layout_width="wrap_content" android:layout_height="match_parent" android:orientation="vertical" > <TextView android:layout_width="100dp" android:layout_height="100dp" android:text="试试1" /> <TextView android:layout_width="100dp" android:layout_height="100dp" android:text="试试2" /> <TextView android:layout_width="100dp" android:layout_height="100dp" android:text="试试3" /> <TextView android:layout_width="100dp" android:layout_height="100dp" android:text="试试4" /> <TextView android:layout_width="100dp" android:layout_height="100dp" android:text="试试5" /> <TextView android:layout_width="100dp" android:layout_height="100dp" android:text="试试6" /> </LinearLayout> </com.example.demo1.ObservableScrollView> </LinearLayout>
package com.example.demo1; import android.os.Bundle; import android.app.Activity; import android.view.Menu; public class MainActivity extends Activity implements ScrollViewListener { private ObservableScrollView scrollView1 = null; private ObservableScrollView scrollView2 = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); scrollView1 = (ObservableScrollView) findViewById(R.id.view1); scrollView1.setScrollViewListener(this); scrollView2 = (ObservableScrollView) findViewById(R.id.view2); scrollView2.setScrollViewListener(this); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } @Override public void onScrollChanged(ObservableScrollView scrollView, int x, int y, int oldx, int oldy) { if (scrollView == scrollView1) { scrollView2.scrollTo(x, y); } else if (scrollView == scrollView2) { scrollView1.scrollTo(x, y); } } }
上面这种方法主要是通过重写ScrollView来实现的,在主程序中无需再做额外操作。另外,还要一个方法就是不改动ScrollView,而是在主程序中进行操作,方式就是在触摸监听事件中,设立对手势的监听,在手指离开屏幕后用
handler.sendMessageDelayed( handler.obtainMessage(TOUCH_EVENT_ID,scroller), 5);每隔5毫秒getScrollY()一次,然后比较getScrollY()的值,直到与上次得到的相等时,就是scrollview停止滑动了。
mScrollView.setOnTouchListener(new OnTouchListener() { private int lastY = 0; private static final int TOUCH_EVENT_ID = 1; @Override public boolean onTouch(View v, MotionEvent event) { // TODO Auto-generated method stub if(event.getAction() == MotionEvent.ACTION_UP) {//触摸松手时 handler.sendMessageDelayed(handler.obtainMessage(TOUCH_EVENT_ID,v), 5); } return false; } Handler handler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); View scroller = (View)msg.obj; if(msg.what==TOUCH_EVENT_ID) { if(lastY ==scroller.getScrollY()) { handleStop(scroller);//调用停止时处理方法 }else { handler.sendMessageDelayed( handler.obtainMessage(TOUCH_EVENT_ID,scroller), 5); lastY = scroller.getScrollY(); } } } };//这里写真正的事件
private void handleStop(Object view) {//滑动停止时要进行的处理 ScrollView scroller = (ScrollView) view; scroller.scrollTo(0, mScrollOffset); }
当然,也可以把他们都封装在一起,方便使用:
import android.content.Context; import android.os.Handler; import android.os.Message; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import android.widget.ScrollView; public class LazyScrollView extends ScrollView{ private static final String tag="LazyScrollView"; private Handler handler; private View view; public LazyScrollView(Context context) { super(context); // TODO Auto-generated constructor stub } public LazyScrollView(Context context, AttributeSet attrs) { super(context, attrs); // TODO Auto-generated constructor stub } public LazyScrollView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); // TODO Auto-generated constructor stub } //这个获得总的高度 public int computeVerticalScrollRange(){ return super.computeHorizontalScrollRange(); } public int computeVerticalScrollOffset(){ return super.computeVerticalScrollOffset(); } private void init(){ this.setOnTouchListener(onTouchListener); handler=new Handler(){ @Override public void handleMessage(Message msg) { // process incoming messages here super.handleMessage(msg); switch(msg.what){ case 1: if(view.getMeasuredHeight() <= getScrollY() + getHeight()) { if(onScrollListener!=null){ onScrollListener.onBottom(); } }else if(getScrollY()==0){ if(onScrollListener!=null){ onScrollListener.onTop(); } } else{ if(onScrollListener!=null){ onScrollListener.onScroll(); } } break; default: break; } } }; } OnTouchListener onTouchListener=new OnTouchListener(){ @Override public boolean onTouch(View v, MotionEvent event) { // TODO Auto-generated method stub switch (event.getAction()) { case MotionEvent.ACTION_DOWN: break; case MotionEvent.ACTION_UP: if(view!=null&&onScrollListener!=null){ handler.sendMessageDelayed(handler.obtainMessage(1), 200); } break; default: break; } return false; } }; /** * 获得参考的View,主要是为了获得它的MeasuredHeight,然后和滚动条的ScrollY+getHeight作比较。 */ public void getView(){ this.view=getChildAt(0); if(view!=null){ init(); } } /** * 定义接口 * @author admin * */ public interface OnScrollListener{ void onBottom(); void onTop(); void onScroll(); } private OnScrollListener onScrollListener; public void setOnScrollListener(OnScrollListener onScrollListener){ this.onScrollListener=onScrollListener; } }通常这样使用:
scrollView=(LazyScrollView)findViewById(R.id.scrollView); scrollView.getView(); scrollView.setOnScrollListener(new OnScrollListener() { @Override public void onTop() { // TODO Auto-generated method stub Log.d(tag,"------滚动到最上方------"); } @Override public void onScroll() { // TODO Auto-generated method stub Log.d(tag,"没有到最下方,也不是最上方"); } @Override public void onBottom() { // TODO Auto-generated method stub Log.d(tag,"------滚动到最下方------"); } });