Android中左右滑屏实现

在网上搜索了下滑屏的实现,自己整理了下,

代码如下:

package kexc.scroll;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.Scroller;

/**
 * 仿Launcher中的WorkSapce,可以左右滑动切换屏幕的类
 * 
 */
public class ScrollLayout extends ViewGroup {

/*
  * onMeasure方法在控件的父元素正要放置它的子控件时调用。它会问一个问题,“你想要用多大地方啊?”,然后传入两个参数——
  * widthMeasureSpec和heightMeasureSpec。它们指明控件可获得的空间以及关于这个空间描述的元数据。
  * 比返回一个结果要好的方法是你传递View的高度和宽度到setMeasuredDimension方法里。
  * 一个MeasureSpec包含一个尺寸和模式。
  * 有三种可能的模式: 
  * UNSPECIFIED:父布局没有给子布局任何限制,子布局可以任意大小。
  * EXACTLY:父布局决定子布局的确切大小。不论子布局多大,它都必须限制在这个界限里。
  * AT_MOST:子布局可以根据自己的大小选择任意大小。
  */

/*
  * VelocityTracker类
  * 
  * 功能: 根据触摸位置计算每像素的移动速率。
  * 
  * 常用方法有:
  * 
  * public void addMovement (MotionEvent ev) 功能:添加触摸对象MotionEvent , 用于计算触摸速率。
  * public void computeCurrentVelocity (int units)
  * 功能:以每像素units单位考核移动速率。额,其实我也不太懂,赋予值1000即可。 参照源码 该units的意思如下: 参数 units :
  * The units you would like the velocity in. A value of 1 provides pixels
  * per millisecond, 1000 provides pixels per second, etc. public float
  * getXVelocity () 功能:获得X轴方向的移动速率。
  */
 /*
  * ViewConfiguration类
  * 
  * 功能: 获得一些关于timeouts(时间)、sizes(大小)、distances(距离)的标准常量值 。
  * 
  * 常用方法:
  * 
  * public int getScaledEdgeSlop()
  * 
  * 说明:获得一个触摸移动的最小像素值。也就是说,只有超过了这个值,才代表我们该滑屏处理了。
  * 
  * public static int getLongPressTimeout()
  * 
  * 说明:获得一个执行长按事件监听(onLongClickListener)的值。也就是说,对某个View按下触摸时,只有超过了
  * 
  * 这个时间值在,才表示我们该对该View回调长按事件了;否则,小于这个时间点松开手指,只执行onClick监听
  */
 private static final String TAG = "ScrollLayout";
 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;// change 多少像素算是发生move操作
 private float mLastMotionX;
 private float mLastMotionY;

public ScrollLayout(Context context, AttributeSet attrs) {
  this(context, attrs, 0);
  // TODO Auto-generated constructor stub
 }

public ScrollLayout(Context context, AttributeSet attrs, int defStyle) {
  super(context, attrs, defStyle);
  // TODO Auto-generated constructor stub
  mScroller = new Scroller(context);

mCurScreen = mDefaultScreen;
  //初始化一个最小滑动距离
  mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
 }

/**
  * 生成view
  */
 @Override
 protected void onLayout(boolean changed, int l, int t, int r, int b) {
  // TODO Auto-generated method stub
  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) {
     final int childWidth = childView.getMeasuredWidth();
     childView.layout(childLeft, 0, childLeft + childWidth,
       childView.getMeasuredHeight());
     childLeft += childWidth;
    }
   }
  }
 }

@Override
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  Log.e(TAG, "onMeasure");
  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);
 }

/**
  * According to the position of current layout scroll to the destination
  * page.
  */
 public void snapToDestination() {
  // 判断是否超过下一屏的中间位置,如果达到就抵达下一屏,否则保持在原屏幕
  // 这样的一个简单公式意思是:假设当前滑屏偏移值即 scrollCurX 加上每个屏幕一半的宽度,除以每个屏幕的宽度就是
  // 我们目标屏所在位置了。 假如每个屏幕宽度为320dip, 我们滑到了500dip处,很显然我们应该到达第二屏,索引值为1
  // 即(500 + 320/2)/320 = 1
  final int screenWidth = getWidth();
  final int destScreen = (getScrollX() + screenWidth / 2) / screenWidth;
  snapToScreen(destScreen);
 }

public void snapToScreen(int whichScreen) {
  // 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) * 5);
   mCurScreen = whichScreen;
   onScreenChangeListener.onScreenChange(mCurScreen);
   invalidate(); // Redraw the layout
  }
 }

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

public int getCurScreen() {
  return mCurScreen;
 }

/**
  * 控制view跟随手指滑动 由父视图调用用来请求子视图根据偏移值 mScrollX,mScrollY重新绘制
  */
 @Override
 public void computeScroll() {
  // 如果返回true,表示动画还没有结束
  // 因为前面startScroll,所以只有在startScroll完成时 才会为false
  if (mScroller.computeScrollOffset()) {
   // 产生了动画效果,根据当前值 每次滚动一点
   scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
   postInvalidate();
  }
 }

/*
  * 其中:onInterceptTouchEvent()主要功能是控制触摸事件的分发,例如是子视图的点击事件还是滑动事件。
  * 其他所有处理过程均在onTouchEvent()方法里实现了。 1、屏幕的滑动要根据手指的移动而移动 ----
  * 主要实现在onTouchEvent()方法中
  * 
  * 2、当手指松开时,可能我们并没有完全滑动至某个屏幕上,这是我们需要手动判断当前偏移至去计算目标屏(当前屏或者
  * 
  * 前后屏),并且优雅的偏移到目标屏(当然是用Scroller实例咯)。
  * 
  * 3、调用computeScroll ()去实现缓慢移动过程。
  */
 @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
    // onScreenChangeListener.onScreenChange(mCurScreen - 1);
    Log.e(TAG, "snap left");
    snapToScreen(mCurScreen - 1);
   } else if (velocityX < -SNAP_VELOCITY
     && mCurScreen < getChildCount() - 1) {
    //快速向左滑屏,返回下一个屏幕)  
    // Fling enough to move right
    Log.e(TAG, "snap right");
    // onScreenChangeListener.onScreenChange(mCurScreen + 1);
    snapToScreen(mCurScreen + 1);
   }
   //以上为快速移动的 ,强制切换屏幕 
   else {
    //缓慢移动的,因此先判断是保留在本屏幕还是到下一屏幕
    snapToDestination();
   }

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

return true;
 }

/**
  * touch 事件是否继续从父控件向子空间传递 true 不传递
  */
 @Override
 public boolean onInterceptTouchEvent(MotionEvent ev) {
  // TODO Auto-generated method stub
  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:
   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 OnScreenChangeListener {
  void onScreenChange(int currentIndex);
 }

private OnScreenChangeListener onScreenChangeListener;

public void setOnScreenChangeListener(
   OnScreenChangeListener onScreenChangeListener) {
  this.onScreenChangeListener = onScreenChangeListener;
 }

}

package kexc.scroll;

import kexc.scroll.ScrollLayout.OnScreenChangeListener;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.ImageView;
import android.widget.LinearLayout;

public class PageControlView extends LinearLayout {

private Context context;
 private int count;

public PageControlView(Context context) {
  super(context);
  this.context = context;
 }

public PageControlView(Context context, AttributeSet attrs) {
  super(context, attrs);
  this.context = context;
 }

public void bindScrollLayout(ScrollLayout scrollLayout) {
  this.count = scrollLayout.getChildCount();
  Log.v("=count==", count + "=");
  // 初始化第一屏
  generatePageControl(0);
  scrollLayout.setOnScreenChangeListener(new OnScreenChangeListener() {

@Override
   public void onScreenChange(int currentIndex) {

generatePageControl(currentIndex);
   }
  });
 }

private void generatePageControl(int currentIndex) {
  this.removeAllViews();
  for (int i = 0; i < this.count; i++) {
   Log.v("=i==", i + "=");
   ImageView imageView = new ImageView(context);
   LinearLayout.LayoutParams llp = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT,
     LayoutParams.WRAP_CONTENT);
   llp.setMargins(10, 0, 0, 10);
   imageView.setLayoutParams(llp);
   if (i == currentIndex) {
    imageView.setImageResource(R.drawable.page_indicator_focused);
   } else {

imageView.setImageResource(R.drawable.page_indicator);
   }
   this.addView(imageView);
  }
 }

}

package kexc.scroll;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnFocusChangeListener;
import android.widget.Button;

public class ScrollLayoutActivity extends Activity {
 private ScrollLayout sl;
 private Button btn1;
 private PageControlView pageControlView;

@Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.main);
  sl = (ScrollLayout) findViewById(R.id.ScrollLayoutTest);
  pageControlView = (PageControlView) findViewById(R.id.pageControl);
  btn1 = (Button) findViewById(R.id.btn1);
  btn1.setOnClickListener(new OnClickListener() {

@Override
   public void onClick(View v) {
    sl.snapToScreen(1);
   }
  });
  sl.setOnFocusChangeListener(new OnFocusChangeListener() {

@Override
   public void onFocusChange(View v, boolean hasFocus) {
    int i = v.getVisibility();
    Log.v("=sl.i=", i + "=");
   }
  });
  Log.v("=sl.getCurScreen()=", sl.getCurScreen() + "=");
  pageControlView.bindScrollLayout(sl);
 }
}

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" >

<kexc.scroll.ScrollLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/ScrollLayoutTest"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" >

<LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:background="#FF00" >
        </LinearLayout>

<FrameLayout
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:background="#F0F0" >
        </FrameLayout>

<FrameLayout
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:background="#F00F" >
        </FrameLayout>

<LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:background="#FF00" >

<Button
                android:id="@+id/btn1"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Button1" />
        </LinearLayout>

<LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" >

<Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Button2" />
        </LinearLayout>
    </kexc.scroll.ScrollLayout>

<kexc.scroll.PageControlView
        android:id="@+id/pageControl"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true" >
    </kexc.scroll.PageControlView>

</RelativeLayout>

Android中左右滑屏实现

时间: 2024-12-08 05:35:29

Android中左右滑屏实现的相关文章

ffmpeg在android上输出滑屏问题处理

ffmpeg部分机器上有花屏的问题 原代码如下: while(av_read_frame(formatCtx, &packet)>=0 && !_stop && NULL!=window && bInit) { // Is this a packet from the video stream? if(packet.stream_index==videoStream) { // Decode video frame avcodec_decode

Android中设置全屏的方法

在实际的应用程序开发中,我们有时需要把 Activity 设置成全屏显示,一般情况下,可以通过两种方式来设置全屏显示效果.其一,通过在代码中可以设置,其二,通过manifest配置文件来设置全屏. 其一:在代码中设置(如下) public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //设置无标题 requestWindowFeature(Window.FEATURE_NO_TITL

Android中的全屏

全屏一般有两种方法,第一种是在festxml文件中配置: 也就是给activity节点加上android:theme="@android:style/Theme.NoTitleBar.Fullscreen": <activity android:name="com.howlaa.girlsos.FirstActivity" android:label="@string/app_name" android:theme="@andro

Android中实现全屏、无标题栏的两种办法

在进行UI设计时,我们经常需要将屏幕设置成无标题栏或者全屏.要实现起来也非常简单,主要有两种方法:配置xml文件和编写代码设置. 1.在xml文件中进行配置 在项目的清单文件AndroidManifest.xml中,找到需要全屏或设置成无标题栏的Activity,在该Activity进行如下配置即可. 实现全屏效果: android:theme="@android:style/Theme.NoTitleBar.Fullscreen" 实现无标题栏(但有系统自带的任务栏): androi

如何在Android中实现全屏,去掉标题栏效果

在进行Android UI设计时,我们经常需要将屏幕设置成无标题栏或者全屏.要实现起来也非常简单,主要有两种方法:配置xml文件和编写代码设置.   1.在xml文件中进行配置   在项目的清单文件AndroidManifest.xml中,找到需要全屏或设置成无标题栏的Activity,在该Activity进行如下配置即可.   实现全屏效果: android:theme="@android:style/Theme.NoTitleBar.Fullscreen" 实现无标题栏(但有系统自

Android中关于横竖屏问题

一.屏幕的方向可以在AndroidManifest.xml文件中通过设置screenOrientation属性来实现, screenOrientation用来指定Activity的在设备上显示的方向,每个值代表如下含义: "unspecified" 默认值 由系统来判断显示方向.判定的策略是和设备相关的,所以不同的设备会有不同的显示方向. "landscape" 横屏显示(宽比高要长) "portrait" 竖屏显示(高比宽要长) "u

Android中锁屏密码算法解析以及破解方案

一.前言 最近玩王者荣耀,下载了一个辅助样本,结果被锁机了,当然破解它很简单,这个后面会详细分析这个样本,但是因为这个样本引发出的欲望就是解析Android中锁屏密码算法,然后用一种高效的方式制作锁机恶意样本.现在的锁机样本原理强制性太过于复杂,没意义.所以本文就先来介绍一下android中的锁屏密码算法原理. 二.锁屏密码方式 我们知道Android中现结单支持的锁屏密码主要有两种: 一种是手势密码,也就是我们常见的九宫格密码图 一种是输入密码,这个也分为PIN密码和复杂字符密码,而PIN密码

android 中activity 属性说明(转载)

转自:http://liuwuhen.iteye.com/blog/1759796 activity是android中使用非常平凡的一种组件,我们除了需要掌握activity中的生命周期以外,还需要掌握activity中的其 他设置. 1.activity 中主题的设置   android中我们可以通过AndroidManifest.xml中设置activity中的主题,其实android提供了许多自带的主题样式,如下: android:theme="@android:style/Theme.D

转-android 中activity 属性说明

activity是android中使用非常平凡的一种组件,我们除了需要掌握activity中的生命周期以外,还需要掌握activity中的其 他设置. 1.activity 中主题的设置   android中我们可以通过AndroidManifest.xml中设置activity中的主题,其实android提供了许多自带的主题样式,如下: android:theme="@android:style/Theme.Dialog"   将一个Activity显示为能话框模式 android: