Android实现图片滚动和页签控件功能的实现代码

首先题外话,今天早上起床的时候,手滑一下把我的手机甩了出去,结果陪伴我两年半的摩托罗拉里程碑一代就这么安息了,于是我今天决定怒更一记,纪念我死去的爱机。

如果你是网购达人,你的手机上一定少不了淘宝客户端。关注特效的人一定都会发现,淘宝不管是网站还是手机客户端,主页上都会有一个图片滚动播放器,上面展示一些它推荐的商品。这个几乎可以用淘宝来冠名的功能,看起来还是挺炫的,我们今天就来实现一下。

实现原理其实还是之前那篇文章Android仿人人客户端滑动菜单的侧滑菜单效果,史上最简单的侧滑实现  ,算是以那个原理为基础的另外一个变种。正所谓一通百通,真正掌握一种方法之后,就可以使用这个方法变换出各种不通的效果。

今天仍然还是实现一个自定义控件,然后我们在任意Activity的布局文件中引用一下,即可实现图片滚动器的效果。

在Eclipse中新建一个Android项目,项目名就叫做SlidingViewSwitcher。

新建一个类,名叫SlidingSwitcherView,这个类是继承自RelativeLayout的,并且实现了OnTouchListener接口,具体代码如下:

public class SlidingSwitcherView extends RelativeLayout implements OnTouchListener {
 /**
 * 让菜单滚动,手指滑动需要达到的速度。
 */
 public static final int SNAP_VELOCITY = 200;
 /**
 * SlidingSwitcherView的宽度。
 */
 private int switcherViewWidth;
 /**
 * 当前显示的元素的下标。
 */
 private int currentItemIndex;
 /**
 * 菜单中包含的元素总数。
 */
 private int itemsCount;
 /**
 * 各个元素的偏移边界值。
 */
 private int[] borders;
 /**
 * 最多可以滑动到的左边缘。值由菜单中包含的元素总数来定,marginLeft到达此值之后,不能再减少。
 *
 */
 private int leftEdge = 0;
 /**
 * 最多可以滑动到的右边缘。值恒为0,marginLeft到达此值之后,不能再增加。
 */
 private int rightEdge = 0;
 /**
 * 记录手指按下时的横坐标。
 */
 private float xDown;
 /**
 * 记录手指移动时的横坐标。
 */
 private float xMove;
 /**
 * 记录手机抬起时的横坐标。
 */
 private float xUp;
 /**
 * 菜单布局。
 */
 private LinearLayout itemsLayout;
 /**
 * 标签布局。
 */
 private LinearLayout dotsLayout;
 /**
 * 菜单中的第一个元素。
 */
 private View firstItem;
 /**
 * 菜单中第一个元素的布局,用于改变leftMargin的值,来决定当前显示的哪一个元素。
 */
 private MarginLayoutParams firstItemParams;
 /**
 * 用于计算手指滑动的速度。
 */
 private VelocityTracker mVelocityTracker;
 /**
 * 重写SlidingSwitcherView的构造函数,用于允许在XML中引用当前的自定义布局。
 *
 * @param context
 * @param attrs
 */
 public SlidingSwitcherView(Context context, AttributeSet attrs) {
 super(context, attrs);
 }
 /**
 * 滚动到下一个元素。
 */
 public void scrollToNext() {
 new ScrollTask().execute(-20);
 }
 /**
 * 滚动到上一个元素。
 */
 public void scrollToPrevious() {
 new ScrollTask().execute(20);
 }
 /**
 * 在onLayout中重新设定菜单元素和标签元素的参数。
 */
 @Override
 protected void onLayout(boolean changed, int l, int t, int r, int b) {
 super.onLayout(changed, l, t, r, b);
 if (changed) {
  initializeItems();
  initializeDots();
 }
 }
 /**
 * 初始化菜单元素,为每一个子元素增加监听事件,并且改变所有子元素的宽度,让它们等于父元素的宽度。
 */
 private void initializeItems() {
 switcherViewWidth = getWidth();
 itemsLayout = (LinearLayout) getChildAt(0);
 itemsCount = itemsLayout.getChildCount();
 borders = new int[itemsCount];
 for (int i = 0; i < itemsCount; i++) {
  borders[i] = -i * switcherViewWidth;
  View item = itemsLayout.getChildAt(i);
  MarginLayoutParams params = (MarginLayoutParams) item.getLayoutParams();
  params.width = switcherViewWidth;
  item.setLayoutParams(params);
  item.setOnTouchListener(this);
 }
 leftEdge = borders[itemsCount - 1];
 firstItem = itemsLayout.getChildAt(0);
 firstItemParams = (MarginLayoutParams) firstItem.getLayoutParams();
 }
 /**
 * 初始化标签元素。
 */
 private void initializeDots() {
 dotsLayout = (LinearLayout) getChildAt(1);
 refreshDotsLayout();
 }
 @Override
 public boolean onTouch(View v, MotionEvent event) {
 createVelocityTracker(event);
 switch (event.getAction()) {
 case MotionEvent.ACTION_DOWN:
  // 手指按下时,记录按下时的横坐标
  xDown = event.getRawX();
  break;
 case MotionEvent.ACTION_MOVE:
  // 手指移动时,对比按下时的横坐标,计算出移动的距离,来调整左侧布局的leftMargin值,从而显示和隐藏左侧布局
  xMove = event.getRawX();
  int distanceX = (int) (xMove - xDown) - (currentItemIndex * switcherViewWidth);
  firstItemParams.leftMargin = distanceX;
  if (beAbleToScroll()) {
  firstItem.setLayoutParams(firstItemParams);
  }
  break;
 case MotionEvent.ACTION_UP:
  // 手指抬起时,进行判断当前手势的意图,从而决定是滚动到左侧布局,还是滚动到右侧布局
  xUp = event.getRawX();
  if (beAbleToScroll()) {
  if (wantScrollToPrevious()) {
   if (shouldScrollToPrevious()) {
   currentItemIndex--;
   scrollToPrevious();
   refreshDotsLayout();
   } else {
   scrollToNext();
   }
  } else if (wantScrollToNext()) {
   if (shouldScrollToNext()) {
   currentItemIndex++;
   scrollToNext();
   refreshDotsLayout();
   } else {
   scrollToPrevious();
   }
  }
  }
  recycleVelocityTracker();
  break;
 }
 return false;
 }
 /**
 * 当前是否能够滚动,滚动到第一个或最后一个元素时将不能再滚动。
 *
 * @return 当前leftMargin的值在leftEdge和rightEdge之间返回true,否则返回false。
 */
 private boolean beAbleToScroll() {
 return firstItemParams.leftMargin < rightEdge && firstItemParams.leftMargin > leftEdge;
 }
 /**
 * 判断当前手势的意图是不是想滚动到上一个菜单元素。如果手指移动的距离是正数,则认为当前手势是想要滚动到上一个菜单元素。
 *
 * @return 当前手势想滚动到上一个菜单元素返回true,否则返回false。
 */
 private boolean wantScrollToPrevious() {
 return xUp - xDown > 0;
 }
 /**
 * 判断当前手势的意图是不是想滚动到下一个菜单元素。如果手指移动的距离是负数,则认为当前手势是想要滚动到下一个菜单元素。
 *
 * @return 当前手势想滚动到下一个菜单元素返回true,否则返回false。
 */
 private boolean wantScrollToNext() {
 return xUp - xDown < 0;
 }
 /**
 * 判断是否应该滚动到下一个菜单元素。如果手指移动距离大于屏幕的1/2,或者手指移动速度大于SNAP_VELOCITY,
 * 就认为应该滚动到下一个菜单元素。
 *
 * @return 如果应该滚动到下一个菜单元素返回true,否则返回false。
 */
 private boolean shouldScrollToNext() {
 return xDown - xUp > switcherViewWidth / 2 || getScrollVelocity() > SNAP_VELOCITY;
 }
 /**
 * 判断是否应该滚动到上一个菜单元素。如果手指移动距离大于屏幕的1/2,或者手指移动速度大于SNAP_VELOCITY,
 * 就认为应该滚动到上一个菜单元素。
 *
 * @return 如果应该滚动到上一个菜单元素返回true,否则返回false。
 */
 private boolean shouldScrollToPrevious() {
 return xUp - xDown > switcherViewWidth / 2 || getScrollVelocity() > SNAP_VELOCITY;
 }
 /**
 * 刷新标签元素布局,每次currentItemIndex值改变的时候都应该进行刷新。
 */
 private void refreshDotsLayout() {
 dotsLayout.removeAllViews();
 for (int i = 0; i < itemsCount; i++) {
  LinearLayout.LayoutParams linearParams = new LinearLayout.LayoutParams(0,
   LayoutParams.FILL_PARENT);
  linearParams.weight = 1;
  RelativeLayout relativeLayout = new RelativeLayout(getContext());
  ImageView image = new ImageView(getContext());
  if (i == currentItemIndex) {
  image.setBackgroundResource(R.drawable.dot_selected);
  } else {
  image.setBackgroundResource(R.drawable.dot_unselected);
  }
  RelativeLayout.LayoutParams relativeParams = new RelativeLayout.LayoutParams(
   LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
  relativeParams.addRule(RelativeLayout.CENTER_IN_PARENT);
  relativeLayout.addView(image, relativeParams);
  dotsLayout.addView(relativeLayout, linearParams);
 }
 }
 /**
 * 创建VelocityTracker对象,并将触摸事件加入到VelocityTracker当中。
 *
 * @param event
 *  右侧布局监听控件的滑动事件
 */
 private void createVelocityTracker(MotionEvent event) {
 if (mVelocityTracker == null) {
  mVelocityTracker = VelocityTracker.obtain();
 }
 mVelocityTracker.addMovement(event);
 }
 /**
 * 获取手指在右侧布局的监听View上的滑动速度。
 *
 * @return 滑动速度,以每秒钟移动了多少像素值为单位。
 */
 private int getScrollVelocity() {
 mVelocityTracker.computeCurrentVelocity(1000);
 int velocity = (int) mVelocityTracker.getXVelocity();
 return Math.abs(velocity);
 }
 /**
 * 回收VelocityTracker对象。
 */
 private void recycleVelocityTracker() {
 mVelocityTracker.recycle();
 mVelocityTracker = null;
 }
 /**
 * 检测菜单滚动时,是否有穿越border,border的值都存储在{@link #borders}中。
 *
 * @param leftMargin
 *  第一个元素的左偏移值
 * @param speed
 *  滚动的速度,正数说明向右滚动,负数说明向左滚动。
 * @return 穿越任何一个border了返回true,否则返回false。
 */
 private boolean isCrossBorder(int leftMargin, int speed) {
 for (int border : borders) {
  if (speed > 0) {
  if (leftMargin >= border && leftMargin - speed < border) {
   return true;
  }
  } else {
  if (leftMargin <= border && leftMargin - speed > border) {
   return true;
  }
  }
 }
 return false;
 }
 /**
 * 找到离当前的leftMargin最近的一个border值。
 *
 * @param leftMargin
 *  第一个元素的左偏移值
 * @return 离当前的leftMargin最近的一个border值。
 */
 private int findClosestBorder(int leftMargin) {
 int absLeftMargin = Math.abs(leftMargin);
 int closestBorder = borders[0];
 int closestMargin = Math.abs(Math.abs(closestBorder) - absLeftMargin);
 for (int border : borders) {
  int margin = Math.abs(Math.abs(border) - absLeftMargin);
  if (margin < closestMargin) {
  closestBorder = border;
  closestMargin = margin;
  }
 }
 return closestBorder;
 }
 class ScrollTask extends AsyncTask<Integer, Integer, Integer> {
 @Override
 protected Integer doInBackground(Integer... speed) {
  int leftMargin = firstItemParams.leftMargin;
  // 根据传入的速度来滚动界面,当滚动穿越border时,跳出循环。
  while (true) {
  leftMargin = leftMargin + speed[0];
  if (isCrossBorder(leftMargin, speed[0])) {
   leftMargin = findClosestBorder(leftMargin);
   break;
  }
  publishProgress(leftMargin);
  // 为了要有滚动效果产生,每次循环使线程睡眠10毫秒,这样肉眼才能够看到滚动动画。
  sleep(10);
  }
  return leftMargin;
 }
 @Override
 protected void onProgressUpdate(Integer... leftMargin) {
  firstItemParams.leftMargin = leftMargin[0];
  firstItem.setLayoutParams(firstItemParams);
 }
 @Override
 protected void onPostExecute(Integer leftMargin) {
  firstItemParams.leftMargin = leftMargin;
  firstItem.setLayoutParams(firstItemParams);
 }
 }
 /**
 * 使当前线程睡眠指定的毫秒数。
 *
 * @param millis
 *  指定当前线程睡眠多久,以毫秒为单位
 */
 private void sleep(long millis) {
 try {
  Thread.sleep(millis);
 } catch (InterruptedException e) {
  e.printStackTrace();
 }
 }
}

细心的朋友可以看出来,我还是重用了很多之前的代码,这里有几个重要点我说一下。在onLayout方法里,重定义了各个包含图片的控件的大小,然后为每个包含图片的控件都注册了一个touch事件监听器。这样当我们滑动任何一样图片控件的时候,都会触发onTouch事件,然后通过改变第一个图片控件的leftMargin,去实现动画效果。之后在onLayout里又动态加入了页签View,有几个图片控件就会加入几个页签,然后根据currentItemIndex来决定高亮显示哪一个页签。其它也没什么要特别说明的了,更深的理解大家去看代码和注释吧。

然后看一下布局文件中如何使用我们自定义的这个控件,创建或打开activity_main.xml,里面加入如下代码:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools"
 android:layout_width="fill_parent"
 android:layout_height="fill_parent"
 android:orientation="horizontal"
 tools:context=".MainActivity" >
 <com.example.viewswitcher.SlidingSwitcherView
 android:id="@+id/slidingLayout"
 android:layout_width="fill_parent"
 android:layout_height="100dip" >
 <LinearLayout
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  android:orientation="horizontal" >
  <Button
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  android:background="@drawable/image1" />
  <Button
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  android:background="@drawable/image2" />
  <Button
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  android:background="@drawable/image3" />
  <Button
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  android:background="@drawable/image4" />
 </LinearLayout>
 <LinearLayout
  android:layout_width="60dip"
  android:layout_height="20dip"
  android:layout_alignParentBottom="true"
  android:layout_alignParentRight="true"
  android:layout_margin="15dip"
  android:orientation="horizontal" >
 </LinearLayout>
 </com.example.viewswitcher.SlidingSwitcherView>
</LinearLayout> 

我们可以看到,com.example.viewswitcher.SlidingSwitcherView的根目录下放置了两个LinearLayout。第一个LinearLayout中要放入需要滚动显示的图片,这里我们加入了四个Button,每个Button都设置了一张背景图片。第二个LinearLayout中不需要加入任何东西,只要控制好大小和位置,标签会在运行的时候自动加入到这个layout中。

然后创建或打开MainActivity作为主界面,里面没有加入任何新增的代码:

public class MainActivity extends Activity {
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 }
}

最后是给出AndroidManifest.xml的代码,也都是自动生成的内容:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
 package="com.example.viewswitcher"
 android:versionCode="1"
 android:versionName="1.0" >
 <uses-sdk
 android:minSdkVersion="8"
 android:targetSdkVersion="8" />
 <application
 android:allowBackup="true"
 android:icon="@drawable/ic_launcher"
 android:label="@string/app_name"
 android:theme="@android:style/Theme.NoTitleBar" >
 <activity
  android:name="com.example.viewswitcher.MainActivity"
  android:label="@string/app_name" >
  <intent-filter>
  <action android:name="android.intent.action.MAIN" />
  <category android:name="android.intent.category.LAUNCHER" />
  </intent-filter>
 </activity>
 </application>
</manifest>

好了,现在我们来看下运行效果吧,由于手机坏了,只能在模拟器上运行了。

首先是程序打开的时候,界面显示如下:

然后手指在图片上滑动,我们可以看到图片滚动的效果:

不停的翻页,页签也会跟着一起改变,下图中我们可以看到高亮显示的点是变换的:

恩,对比一下淘宝客户端的效果,我觉得我们模仿的还是挺好的。咦,好像少了点什么。。。。。。原来图片并不会自动播放。。。。。

没关系,我在后面的一篇文章中补充了自动播放这个功能,而且不仅仅是自动播放功能喔,请参考 Android使用自定义属性实现图片自动播放滚动的功能。

今天的文章就到这里了,有问题的朋友请在下面留言。

源码下载,请点击这里

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

您可能感兴趣的文章:

  • Android仿淘宝商品浏览界面图片滚动效果
  • 图片自动播放器我们修正
  • JS特效实现图片自动播放并可控的效果
  • autoPlay 基于jquery的图片自动播放效果
  • 基于Jquery实现的一个图片滚动切换
  • jquery 圆形旋转图片滚动切换效果
  • JQuery 图片滚动轮播示例代码
  • js实现网站首页图片滚动显示
  • jQuery+css实现图片滚动效果(附源码)
  • jquery实现图片滚动效果的简单实例
  • js+div实现图片滚动效果代码
  • ImageFlow可鼠标控制图片滚动
  • javascript 不间断的图片滚动并可点击
  • js实现鼠标经过时图片滚动停止的方法
  • Android使用自定义属性实现图片自动播放滚动的功能
(0)

相关推荐

  • javascript 不间断的图片滚动并可点击

    css: 复制代码 代码如下: <style type="text/css"> .rollBox { width: 704px; overflow: hidden; padding: 12px 0 5px 6px; } .rollBox .LeftBotton { height: 52px; width: 19px; background: url(jt.gif) no-repeat 11px 0; overflow: hidden; float: left; displa

  • Android仿淘宝商品浏览界面图片滚动效果

    用手机淘宝浏览商品详情时,商品图片是放在后面的,在第一个ScrollView滚动到最底下时会有提示,继续拖动才能浏览图片.仿照这个效果写一个出来并不难,只要定义一个Layout管理两个ScrollView就行了,当第一个ScrollView滑到底部时,再次向上滑动进入第二个ScrollView.效果如下: 需要注意的地方是: 1.如果是手动滑到底部需要再次按下才能继续往下滑,自动滚动到底部则不需要 2.在由上一个ScrollView滑动到下一个ScrollView的过程中多只手指相继拖动也不会导

  • jquery 圆形旋转图片滚动切换效果

    这个效果比较特别,可爱,所以在外面网站没怎么看到过,有兴趣的朋友可以下载后自己使用. PS: 经过修改已经兼容大众浏览器.效果图:在线演示:http://demo.jb51.net/js/ImagesRotateScroll/index.htmlStep1. 创建HTML 复制代码 代码如下: <div id="rotatescroll"> <div class="viewport"> <ul class="overview&

  • jquery实现图片滚动效果的简单实例

    复制代码 代码如下: <!DOCTYPE html><html><head>    <meta charset="utf-8">       <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />       <title>jquery 图片自动无缝滚动</title>      

  • JQuery 图片滚动轮播示例代码

    完整的项目在附件中 复制代码 代码如下: <!DOCTYPE html> <html> <head> <title>图片切换</title> <script type="text/javascript" src="js/jquery-1.11.0.min.js"></script> <script type="text/javascript" > va

  • js实现鼠标经过时图片滚动停止的方法

    本文实例讲述了js实现鼠标经过时图片滚动停止的方法.分享给大家供大家参考.具体实现方法如下: 复制代码 代码如下: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml

  • autoPlay 基于jquery的图片自动播放效果

    效果图:实现代码: 复制代码 代码如下: <html> <head> <title>Jquery 自动轮播效果</title> <script src="js/jquery-1.6.2.min.js" type="text/javascript"></script> <style> .spanhide{display: none;} #top a:hover{color: red;}

  • js实现网站首页图片滚动显示

    复制代码 代码如下: <div id="demo"> <div id="indemo"> <div id="demo1"> <asp:Repeater ID="Pro_List" runat="server"> <ItemTemplate> <a href="/Product/html/<%#Eval("id&quo

  • 基于Jquery实现的一个图片滚动切换

    首先还是要引用jquery框架的. 然后开始HTML代码: 复制代码 代码如下: <div id="New_zlMimgMv"> <div class="imgMvBox"> <ul id="imgMvCon"> <li><a href="#" title=""><img src="New_zlimgMv.jpg" alt

  • ImageFlow可鼠标控制图片滚动

    You are free to use this in any product, or on any web site. For more information about ImageFlow read the Documentation and check my Newsblog. For anything else simply drop me a line in my Shoutbox. ChangeLog Version 0.8  Added Safari 1.x compatibil

  • 图片自动播放器脚本之家修正

    图片自动播放器我们修正,可以兼容ff,发现些小问题,希望大家多多讨论,使用方法呢 复制代码 代码如下: ImgName=new ImgArray(3);  ImgName[0]="4.jpg";  ImgName[1]="1.jpg";  ImgName[2]="2.jpg";  ImgName[3]="3.jpg"; 只需修改上面的地方即可,加了图片预载,转载请写明出处,剩下的大家来完善吧 图片自动播放器我们修正 funct

  • jQuery+css实现图片滚动效果(附源码)

    源码下载 bxCarousel参数说明: move:每次滚动移动图片的数量,默认为4. display_num:展示图片的数量,默认为4. speed:图片滚动速度,默认为500毫秒. margin:图片间的间距,默认为0. auto:是否自动滚动,默认为false. auto_interval:当设为自动滚动时,每次滚动的时间间隔(毫秒),默认为2000毫秒即2秒. auto_dir:自动滚动的方向,默认为next,你可以试下prev. next_image:向右滚方向按钮图片,可以用CSS设

  • JS特效实现图片自动播放并可控的效果

    不多说了,实现方法请看下面代码. 代码如下: <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta charset="utf-8"/> <title>JS代码实现图片自动播放并可控的效果</title><base target="_blank" /> <link re

  • js+div实现图片滚动效果代码

    横向 <div id=demo style="overflow:hidden;width:200px;border:2px solid #e0e0e0;padding:2px;" onmouseover="stopscroll();" onmouseout="doscroll()"> <div id="demo1" style="white-space:nowrap;padding:0;"

  • Android使用自定义属性实现图片自动播放滚动的功能

    大家好,记得上次我带着大家一起实现了一个类似与淘宝客户端中带有的图片滚动播放器的效果,但是在做完了之后,发现忘了加入图片自动播放的功能(或许是我有意忘记加的.....),结果图片只能通过手指滑动来播放.于是今天我将再次带领大家,添加上之前遗漏的功能,让我们的图片播放器更加完善. 这次的程序开发将完全基于上一次的代码,如果有朋友还未看过上篇文章,请先阅读Android实现图片滚动和页签控件功能的实现代码. 既然是要加入自动播放的功能,那么就有一个非常重要的问题需要考虑.如果当前已经滚动到了最后一张

随机推荐