Android之ScrollLayout左右滑动效果实现

来源:互联网 发布:洛阳平安数据科技招聘 编辑:程序博客网 时间:2024/05/18 03:26

本篇文章使用一个小程序实现Android之ScrollLayout左右滑动效果


第一步:新建Android工程

添加图片资源,并设置效果

<?xml version="1.0" encoding="utf-8"?><selector  xmlns:android="http://schemas.android.com/apk/res/android">    <item android:state_enabled="true" android:drawable="@drawable/page_indicator_unfocused" />    <item android:state_enabled="false" android:drawable="@drawable/page_indicator_focused" /></selector>

第二步:添加实现左右滑动效果的源文件

1.视图改变接口

public interface OnViewChangeListener {public void OnViewChange(int view);}


2.实现左右滑动效果的源文件,以后我们可以直接使用这个文件

public class MyScrollLayout extends ViewGroup{private static final String TAG = "ScrollLayout";private VelocityTracker mVelocityTracker;// 用于判断甩动手势private Scroller mScroller;// 滑动控制private int mCurScreen;private int mDefaultScreen = 0;private float mLastMotionX; // 手指移动的时候,或者手指离开屏幕的时候记录下的手指的横坐标private float mLastMotionY; // 手指移动的时候,或者手指离开屏幕的时候记录下的手指的纵坐标private int mTouchSlop;// 手指移动的最小距离的判断标准// =ViewConfiguration.get(getContext()).getScaledTouchSlop();// 在viewpapper中就是依赖于这个值来判断用户// 手指滑动的距离是否达到界面滑动的标准private static final int SNAP_VELOCITY = 600;// 默认的滚动速度,之后用于和手指滑动产生的速度比较,获取屏幕滚动的速度private static final int TOUCH_STATE_REST = 0;//表示触摸状态为空闲,即没有触摸或者手指离开了private static final int TOUCH_STATE_SCROLLING = 1;//表示手指正在移动private int mTouchState = TOUCH_STATE_REST;// 当前手指的事件状态private OnViewChangeListener mOnViewChangeListener;public MyScrollLayout(Context context){super(context);Log.i(TAG, "---ScrollLyout1---");init(context);}public MyScrollLayout(Context context, AttributeSet attrs){super(context, attrs);Log.i(TAG, "---ScrollLyout2---");init(context);}public MyScrollLayout(Context context, AttributeSet attrs, int defStyle){super(context, attrs, defStyle);Log.i(TAG, "---ScrollLyout3---");init(context);}private void init(Context context){mCurScreen = mDefaultScreen;mScroller = new Scroller(context);mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); // 使用系统默认的值}@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b){Log.i(TAG, "---onLayout---");// 为每一个孩子设置它们的位置if(changed){int childLeft = 0;final int childCount = getChildCount();for(int i=0; i<childCount; i++){final View childView = getChildAt(i);if (childView.getVisibility() != View.GONE){// 此处获取到的宽度就是在onMeasure中设置的值final int childWidth = childView.getMeasuredWidth();// 为每一个子View布局childView.layout(childLeft, 0, childLeft+childWidth, childView.getMeasuredHeight());childLeft = childLeft + childWidth;}}}}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){super.onMeasure(widthMeasureSpec, heightMeasureSpec);Log.i(TAG, "---onMeasure---");// 在onlayout之前执行,获取View申请的大小,把它们保存下来,方面后面使用final int width = MeasureSpec.getSize(widthMeasureSpec);final int widthMode = MeasureSpec.getMode(widthMeasureSpec);if (widthMode != MeasureSpec.EXACTLY) {throw new IllegalStateException("ScrollLayout only can run at EXACTLY mode!");}final int hightModed = MeasureSpec.getMode(heightMeasureSpec);if (hightModed != MeasureSpec.EXACTLY) {throw new IllegalStateException("ScrollLayout only can run at EXACTLY mode!");}final int count = getChildCount();for(int i=0; i<count; i++){getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);}scrollTo(mCurScreen*width, 0);}/** * 让界面跟着手指移动到手指移动的地点 */public void snapToDestination(){Log.i(TAG, "---snapToDestination---");final int screenWidth = getWidth();// 子view的宽度,此例中为他适配的父view的宽度Log.i(TAG, "screenWidth = "+screenWidth);final int destScreen = (getScrollX()+screenWidth/2)/screenWidth;// 某个算法吧Log.i(TAG, "[destScreen] : "+destScreen);// getScroolX()值为snapToScreen(destScreen);}/** * 滚动到指定screen */public void snapToScreen(int whichScreen){Log.i(TAG, "---snapToDestScreen---");Log.i(TAG, "Math.min(destScreen, getChildCount() - 1) = "+(Math.min(whichScreen, getChildCount() - 1)));whichScreen = Math.max(0,Math.min(whichScreen,getChildCount()-1));// 获取要滚动到的目标screenLog.i(TAG, "whichScreen = "+whichScreen);if (getScrollX() != (whichScreen*getWidth())){final int delta = whichScreen*getWidth()-getScrollX();// 获取屏幕移到目的view还需要移动多少距离Log.i(TAG, "[getScrollX()] : "+getScrollX() );Log.i(TAG, "[delta] : "+delta);Log.i(TAG, "[getScrollX要走到的位置为] : "+(getScrollX()+delta));mScroller.startScroll(getScrollX(), 0, delta, 0, Math.abs(delta)*2);// 使用Scroller辅助滚动,让滚动变得更平滑mCurScreen = whichScreen;invalidate();// 重绘界面if (mOnViewChangeListener != null){mOnViewChangeListener.OnViewChange(mCurScreen);}}}@Overridepublic void computeScroll(){Log.i(TAG, "---computeScroll---");if (mScroller.computeScrollOffset()){scrollTo(mScroller.getCurrX(), mScroller.getCurrY());postInvalidate();//invalidate();}}@Overridepublic boolean onTouchEvent(MotionEvent event){Log.i(TAG, "---onTouchEvent---");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://1,终止滚动2,获取最后一次事件的x值Log.i(TAG, "onTouchEvent:ACTION_DOWN");if (mVelocityTracker == null){mVelocityTracker = VelocityTracker.obtain();mVelocityTracker.addMovement(event);}if (!mScroller.isFinished()){mScroller.abortAnimation();}mLastMotionX = x;break;case MotionEvent.ACTION_MOVE://1,获取最后一次事件的x值2,滚动到指定位置Log.i(TAG, "onTouchEvent:ACTION_MOVE");int deltaX = (int)(mLastMotionX - x);if(IsCanMove(deltaX)){if (mVelocityTracker != null){mVelocityTracker.addMovement(event);}mLastMotionX = x;scrollBy(deltaX, 0);}break;case MotionEvent.ACTION_UP://1,计算手指移动的速度并得出我们需要的速度2,选择不同情况下滚动到哪个screenLog.i(TAG, "onTouchEvent:ACTION_UP");int velocityX = 0;if (mVelocityTracker != null){mVelocityTracker.addMovement(event);mVelocityTracker.computeCurrentVelocity(1000);// 设置属性为计算1秒运行多少个像素// computeCurrentVelocity(int units, float maxVelocity)上面的1000即为此处的units。// maxVelocity必须为正,表示当计算出的速率大于maxVelocity时为maxVelocity,小于maxVelocity就为计算出的速率velocityX = (int)mVelocityTracker.getXVelocity();Log.i(TAG, "[velocityX] : "+velocityX);}if (velocityX > SNAP_VELOCITY && mCurScreen > 0)//如果速度为正,则表示向右滑动。需要指定mCurScreen大于0,才能滑,不然就不准确啦{// Fling enough to move leftLog.i(TAG, "snap left");Log.i(TAG, "速度为正且-->:当前mCurScreen = "+mCurScreen);Log.i(TAG, "要走到的:mCurScreen = "+(mCurScreen-1));snapToScreen(mCurScreen - 1);}else if (velocityX < -SNAP_VELOCITY && mCurScreen < getChildCount() - 1)//如果速度为负,则表示手指向左滑动。需要指定mCurScreen小于最后一个子view的id,才能滑,不然就不准确啦{// Fling enough to move rightLog.i(TAG, "snap right");Log.i(TAG, "速度为fu且《--:当前mCurScreen = "+mCurScreen);Log.i(TAG, "要走到的:mCurScreen = "+(mCurScreen+1));snapToScreen(mCurScreen + 1);}else//速度小于我们规定的达标速度,那么就让界面跟着手指滑动显示。最后显示哪个screen再做计算(方法中有计算){Log.i(TAG, "速度的绝对值小于规定速度,走snapToDestination方法");snapToDestination();}if (mVelocityTracker != null){mVelocityTracker.recycle();mVelocityTracker = null;}//mTouchState = TOUCH_STATE_REST;//为什么这里要设置???break;case MotionEvent.ACTION_CANCEL://1,设置触摸事件状态为空闲Log.i(TAG, "onTouchEvent:ACTION_CANCEL");mTouchState = TOUCH_STATE_REST;break;default:break;}return true;}private boolean IsCanMove(int deltaX){if (getScrollX() <= 0 && deltaX < 0){return false;}if (getScrollX() >= (getChildCount() - 1)* getWidth() && deltaX > 0){return false;}return true;}public void SetOnViewChangeListener(OnViewChangeListener listener){mOnViewChangeListener = listener;}@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {Log.i(TAG, "---onInterceptTouchEvent---");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_DOWN:// 判断滚动是否停止Log.i(TAG, "onInterceptTouchEvent:ACTION_DOWN");mLastMotionX = x;mLastMotionY = y;mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST: TOUCH_STATE_SCROLLING;break;case MotionEvent.ACTION_MOVE:// 判断是否达成滚动条件Log.i(TAG, "onInterceptTouchEvent:ACTION_MOVE");final int xDiff = (int) Math.abs(mLastMotionX - x);if (xDiff > mTouchSlop) {// 如果该值大于我们规定的最小移动距离则表示界面在滚动mTouchState = TOUCH_STATE_SCROLLING;}break;case MotionEvent.ACTION_UP:// 把状态调整为空闲Log.i(TAG, "onInterceptTouchEvent:ACTION_UP");mTouchState = TOUCH_STATE_REST;break;}// 如果屏幕没有在滚动那么就不消耗这个touch事件return mTouchState != TOUCH_STATE_REST;}}

第三步:添加布局文件

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="fill_parent"    android:layout_height="fill_parent"    android:background="#000000"    android:orientation="vertical" >        <RelativeLayout         android:layout_width="fill_parent"        android:layout_height="45dp"        android:background="#000000"        android:gravity="center_vertical">        <TextView             android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_centerInParent="true"            android:textSize="20sp"            android:textColor="#FFFFFF"            android:text="ScrollLayout"/>    </RelativeLayout>        <RelativeLayout         android:layout_width="fill_parent"        android:layout_height="fill_parent"        android:background="#000000"        android:gravity="center_vertical">        <com.scrolllayoutdemo.MyScrollLayout         xmlns:android="http://schemas.android.com/apk/res/android"        android:id="@+id/ScrollLayout"        android:layout_width="fill_parent"        android:layout_height="fill_parent"        android:visibility="visible">        <RelativeLayout             android:background="#FF0000"            android:layout_width="fill_parent"    android:layout_height="fill_parent">        </RelativeLayout>        <RelativeLayout             android:background="#00FF00"            android:layout_width="fill_parent"    android:layout_height="fill_parent">        </RelativeLayout>        <RelativeLayout             android:background="#0000FF"            android:layout_width="fill_parent"    android:layout_height="fill_parent">        </RelativeLayout>    </com.scrolllayoutdemo.MyScrollLayout>        <LinearLayout         android:layout_below="@id/ScrollLayout"        android:orientation="horizontal"        android:id="@+id/llayout"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_centerHorizontal="true"        android:layout_marginBottom="50.0dip"android:visibility="visible">        <ImageView             android:clickable="true"             android:padding="5.0dip"             android:layout_gravity="center_vertical"             android:layout_width="wrap_content"             android:layout_height="wrap_content"             android:src="@drawable/page_indicator_bg" />        <ImageView             android:clickable="true"             android:padding="5.0dip"             android:layout_gravity="center_vertical"             android:layout_width="wrap_content"             android:layout_height="wrap_content"             android:src="@drawable/page_indicator_bg" />        <ImageView             android:clickable="true"             android:padding="5.0dip"             android:layout_gravity="center_vertical"             android:layout_width="wrap_content"             android:layout_height="wrap_content"             android:src="@drawable/page_indicator_bg" />    </LinearLayout>    </RelativeLayout>    </LinearLayout>

第四步:在主界面实现效果

public class MainActivity extends Activity implements OnViewChangeListener{private MyScrollLayout mScrollLayout;private LinearLayout pointLLayout;private ImageView[] imgs;private int count;private int currentItem;@Overrideprotected void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);}@Overridepublic boolean onCreateOptionsMenu(Menu menu){getMenuInflater().inflate(R.menu.main, menu);return true;}@Overridepublic void onStart(){super.onStart();initView();}private void initView(){ mScrollLayout = (MyScrollLayout)findViewById(R.id.ScrollLayout);pointLLayout = (LinearLayout)findViewById(R.id.llayout);count = mScrollLayout.getChildCount();imgs = new ImageView[count];for(int i = 0; i< count;i++) {imgs[i] = (ImageView) pointLLayout.getChildAt(i);imgs[i].setEnabled(true);imgs[i].setTag(i);}currentItem = 0;imgs[currentItem].setEnabled(false);mScrollLayout.SetOnViewChangeListener(this);}@Overridepublic void onResume(){super.onResume();}@Overridepublic void onPause(){super.onPause();}@Overridepublic void onStop(){super.onStop();}@Overridepublic void onDestroy(){super.onDestroy();}@Overridepublic void OnViewChange(int view){setcurrentPoint(view);}private void setcurrentPoint(int position){if(position<0 || position>count-1 || currentItem == position){return;}imgs[currentItem].setEnabled(true);imgs[position].setEnabled(false);currentItem = position;}}

第五步:查看效果

  


源码下载


0 0
原创粉丝点击