Android实现图片滚动控件,含页签功能,让你的应用像淘宝一样炫起来
首先题外话,今天早上起床的时候,手滑一下把我的手机甩了出去,结果陪伴我两年半的摩托罗拉里程碑一代就这么安息了,于是我今天决定怒更一记,纪念我死去的爱机。
如果你是网购达人,你的手机上一定少不了淘宝客户端。关注特效的人一定都会发现,淘宝不管是网站还是手机客户端,主页上都会有一个图片滚动播放器,上面展示一些它推荐的商品。这个几乎可以用淘宝来冠名的功能,看起来还是挺炫的,我们今天就来实现一下。
实现原理其实还是之前那篇文章Android滑动菜单特效实现,仿人人客户端侧滑效果,史上最简单的侧滑实现,算是以那个原理为基础的另外一个变种。正所谓一通百通,真正掌握一种方法之后,就可以使用这个方法变换出各种不通的效果。
今天仍然还是实现一个自定义控件,然后我们在任意Activity的布局文件中引用一下,即可实现图片滚动器的效果。
在Eclipse中新建一个Android项目,项目名就叫做SlidingViewSwitcher。
新建一个类,名叫SlidingSwitcherView,这个类是继承自RelativeLayout的,并且实现了OnTouchListener接口,具体代码如下:
- publicclassSlidingSwitcherViewextendsRelativeLayoutimplementsOnTouchListener{
- /**
- *让菜单滚动,手指滑动需要达到的速度。
- */
- publicstaticfinalintSNAP_VELOCITY=200;
- /**
- *SlidingSwitcherView的宽度。
- */
- privateintswitcherViewWidth;
- /**
- *当前显示的元素的下标。
- */
- privateintcurrentItemIndex;
- /**
- *菜单中包含的元素总数。
- */
- privateintitemsCount;
- /**
- *各个元素的偏移边界值。
- */
- privateint[]borders;
- /**
- *最多可以滑动到的左边缘。值由菜单中包含的元素总数来定,marginLeft到达此值之后,不能再减少。
- *
- */
- privateintleftEdge=0;
- /**
- *最多可以滑动到的右边缘。值恒为0,marginLeft到达此值之后,不能再增加。
- */
- privateintrightEdge=0;
- /**
- *记录手指按下时的横坐标。
- */
- privatefloatxDown;
- /**
- *记录手指移动时的横坐标。
- */
- privatefloatxMove;
- /**
- *记录手机抬起时的横坐标。
- */
- privatefloatxUp;
- /**
- *菜单布局。
- */
- privateLinearLayoutitemsLayout;
- /**
- *标签布局。
- */
- privateLinearLayoutdotsLayout;
- /**
- *菜单中的第一个元素。
- */
- privateViewfirstItem;
- /**
- *菜单中第一个元素的布局,用于改变leftMargin的值,来决定当前显示的哪一个元素。
- */
- privateMarginLayoutParamsfirstItemParams;
- /**
- *用于计算手指滑动的速度。
- */
- privateVelocityTrackermVelocityTracker;
- /**
- *重写SlidingSwitcherView的构造函数,用于允许在XML中引用当前的自定义布局。
- *
- *@paramcontext
- *@paramattrs
- */
- publicSlidingSwitcherView(Contextcontext,AttributeSetattrs){
- super(context,attrs);
- }
- /**
- *滚动到下一个元素。
- */
- publicvoidscrollToNext(){
- newScrollTask().execute(-20);
- }
- /**
- *滚动到上一个元素。
- */
- publicvoidscrollToPrevious(){
- newScrollTask().execute(20);
- }
- /**
- *在onLayout中重新设定菜单元素和标签元素的参数。
- */
- @Override
- protectedvoidonLayout(booleanchanged,intl,intt,intr,intb){
- super.onLayout(changed,l,t,r,b);
- if(changed){
- initializeItems();
- initializeDots();
- }
- }
- /**
- *初始化菜单元素,为每一个子元素增加监听事件,并且改变所有子元素的宽度,让它们等于父元素的宽度。
- */
- privatevoidinitializeItems(){
- switcherViewWidth=getWidth();
- itemsLayout=(LinearLayout)getChildAt(0);
- itemsCount=itemsLayout.getChildCount();
- borders=newint[itemsCount];
- for(inti=0;i<itemsCount;i++){
- borders[i]=-i*switcherViewWidth;
- Viewitem=itemsLayout.getChildAt(i);
- MarginLayoutParamsparams=(MarginLayoutParams)item.getLayoutParams();
- params.width=switcherViewWidth;
- item.setLayoutParams(params);
- item.setOnTouchListener(this);
- }
- leftEdge=borders[itemsCount-1];
- firstItem=itemsLayout.getChildAt(0);
- firstItemParams=(MarginLayoutParams)firstItem.getLayoutParams();
- }
- /**
- *初始化标签元素。
- */
- privatevoidinitializeDots(){
- dotsLayout=(LinearLayout)getChildAt(1);
- refreshDotsLayout();
- }
- @Override
- publicbooleanonTouch(Viewv,MotionEventevent){
- createVelocityTracker(event);
- switch(event.getAction()){
- caseMotionEvent.ACTION_DOWN:
- //手指按下时,记录按下时的横坐标
- xDown=event.getRawX();
- break;
- caseMotionEvent.ACTION_MOVE:
- //手指移动时,对比按下时的横坐标,计算出移动的距离,来调整左侧布局的leftMargin值,从而显示和隐藏左侧布局
- xMove=event.getRawX();
- intdistanceX=(int)(xMove-xDown)-(currentItemIndex*switcherViewWidth);
- firstItemParams.leftMargin=distanceX;
- if(beAbleToScroll()){
- firstItem.setLayoutParams(firstItemParams);
- }
- break;
- caseMotionEvent.ACTION_UP:
- //手指抬起时,进行判断当前手势的意图,从而决定是滚动到左侧布局,还是滚动到右侧布局
- xUp=event.getRawX();
- if(beAbleToScroll()){
- if(wantScrollToPrevious()){
- if(shouldScrollToPrevious()){
- currentItemIndex--;
- scrollToPrevious();
- refreshDotsLayout();
- }else{
- scrollToNext();
- }
- }elseif(wantScrollToNext()){
- if(shouldScrollToNext()){
- currentItemIndex++;
- scrollToNext();
- refreshDotsLayout();
- }else{
- scrollToPrevious();
- }
- }
- }
- recycleVelocityTracker();
- break;
- }
- returnfalse;
- }
- /**
- *当前是否能够滚动,滚动到第一个或最后一个元素时将不能再滚动。
- *
- *@return当前leftMargin的值在leftEdge和rightEdge之间返回true,否则返回false。
- */
- privatebooleanbeAbleToScroll(){
- returnfirstItemParams.leftMargin<rightEdge&&firstItemParams.leftMargin>leftEdge;
- }
- /**
- *判断当前手势的意图是不是想滚动到上一个菜单元素。如果手指移动的距离是正数,则认为当前手势是想要滚动到上一个菜单元素。
- *
- *@return当前手势想滚动到上一个菜单元素返回true,否则返回false。
- */
- privatebooleanwantScrollToPrevious(){
- returnxUp-xDown>0;
- }
- /**
- *判断当前手势的意图是不是想滚动到下一个菜单元素。如果手指移动的距离是负数,则认为当前手势是想要滚动到下一个菜单元素。
- *
- *@return当前手势想滚动到下一个菜单元素返回true,否则返回false。
- */
- privatebooleanwantScrollToNext(){
- returnxUp-xDown<0;
- }
- /**
- *判断是否应该滚动到下一个菜单元素。如果手指移动距离大于屏幕的1/2,或者手指移动速度大于SNAP_VELOCITY,
- *就认为应该滚动到下一个菜单元素。
- *
- *@return如果应该滚动到下一个菜单元素返回true,否则返回false。
- */
- privatebooleanshouldScrollToNext(){
- returnxDown-xUp>switcherViewWidth/2||getScrollVelocity()>SNAP_VELOCITY;
- }
- /**
- *判断是否应该滚动到上一个菜单元素。如果手指移动距离大于屏幕的1/2,或者手指移动速度大于SNAP_VELOCITY,
- *就认为应该滚动到上一个菜单元素。
- *
- *@return如果应该滚动到上一个菜单元素返回true,否则返回false。
- */
- privatebooleanshouldScrollToPrevious(){
- returnxUp-xDown>switcherViewWidth/2||getScrollVelocity()>SNAP_VELOCITY;
- }
- /**
- *刷新标签元素布局,每次currentItemIndex值改变的时候都应该进行刷新。
- */
- privatevoidrefreshDotsLayout(){
- dotsLayout.removeAllViews();
- for(inti=0;i<itemsCount;i++){
- LinearLayout.LayoutParamslinearParams=newLinearLayout.LayoutParams(0,
- LayoutParams.FILL_PARENT);
- linearParams.weight=1;
- RelativeLayoutrelativeLayout=newRelativeLayout(getContext());
- ImageViewimage=newImageView(getContext());
- if(i==currentItemIndex){
- image.setBackgroundResource(R.drawable.dot_selected);
- }else{
- image.setBackgroundResource(R.drawable.dot_unselected);
- }
- RelativeLayout.LayoutParamsrelativeParams=newRelativeLayout.LayoutParams(
- LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT);
- relativeParams.addRule(RelativeLayout.CENTER_IN_PARENT);
- relativeLayout.addView(image,relativeParams);
- dotsLayout.addView(relativeLayout,linearParams);
- }
- }
- /**
- *创建VelocityTracker对象,并将触摸事件加入到VelocityTracker当中。
- *
- *@paramevent
- *右侧布局监听控件的滑动事件
- */
- privatevoidcreateVelocityTracker(MotionEventevent){
- if(mVelocityTracker==null){
- mVelocityTracker=VelocityTracker.obtain();
- }
- mVelocityTracker.addMovement(event);
- }
- /**
- *获取手指在右侧布局的监听View上的滑动速度。
- *
- *@return滑动速度,以每秒钟移动了多少像素值为单位。
- */
- privateintgetScrollVelocity(){
- mVelocityTracker.computeCurrentVelocity(1000);
- intvelocity=(int)mVelocityTracker.getXVelocity();
- returnMath.abs(velocity);
- }
- /**
- *回收VelocityTracker对象。
- */
- privatevoidrecycleVelocityTracker(){
- mVelocityTracker.recycle();
- mVelocityTracker=null;
- }
- /**
- *检测菜单滚动时,是否有穿越border,border的值都存储在{@link#borders}中。
- *
- *@paramleftMargin
- *第一个元素的左偏移值
- *@paramspeed
- *滚动的速度,正数说明向右滚动,负数说明向左滚动。
- *@return穿越任何一个border了返回true,否则返回false。
- */
- privatebooleanisCrossBorder(intleftMargin,intspeed){
- for(intborder:borders){
- if(speed>0){
- if(leftMargin>=border&&leftMargin-speed<border){
- returntrue;
- }
- }else{
- if(leftMargin<=border&&leftMargin-speed>border){
- returntrue;
- }
- }
- }
- returnfalse;
- }
- /**
- *找到离当前的leftMargin最近的一个border值。
- *
- *@paramleftMargin
- *第一个元素的左偏移值
- *@return离当前的leftMargin最近的一个border值。
- */
- privateintfindClosestBorder(intleftMargin){
- intabsLeftMargin=Math.abs(leftMargin);
- intclosestBorder=borders[0];
- intclosestMargin=Math.abs(Math.abs(closestBorder)-absLeftMargin);
- for(intborder:borders){
- intmargin=Math.abs(Math.abs(border)-absLeftMargin);
- if(margin<closestMargin){
- closestBorder=border;
- closestMargin=margin;
- }
- }
- returnclosestBorder;
- }
- classScrollTaskextendsAsyncTask<Integer,Integer,Integer>{
- @Override
- protectedIntegerdoInBackground(Integer...speed){
- intleftMargin=firstItemParams.leftMargin;
- //根据传入的速度来滚动界面,当滚动穿越border时,跳出循环。
- while(true){
- leftMargin=leftMargin+speed[0];
- if(isCrossBorder(leftMargin,speed[0])){
- leftMargin=findClosestBorder(leftMargin);
- break;
- }
- publishProgress(leftMargin);
- //为了要有滚动效果产生,每次循环使线程睡眠10毫秒,这样肉眼才能够看到滚动动画。
- sleep(10);
- }
- returnleftMargin;
- }
- @Override
- protectedvoidonProgressUpdate(Integer...leftMargin){
- firstItemParams.leftMargin=leftMargin[0];
- firstItem.setLayoutParams(firstItemParams);
- }
- @Override
- protectedvoidonPostExecute(IntegerleftMargin){
- firstItemParams.leftMargin=leftMargin;
- firstItem.setLayoutParams(firstItemParams);
- }
- }
- /**
- *使当前线程睡眠指定的毫秒数。
- *
- *@parammillis
- *指定当前线程睡眠多久,以毫秒为单位
- */
- privatevoidsleep(longmillis){
- try{
- Thread.sleep(millis);
- }catch(InterruptedExceptione){
- e.printStackTrace();
- }
- }
- }
然后看一下布局文件中如何使用我们自定义的这个控件,创建或打开activity_main.xml,里面加入如下代码:
- <LinearLayoutxmlns: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>
然后创建或打开MainActivity作为主界面,里面没有加入任何新增的代码:
- publicclassMainActivityextendsActivity{
- @Override
- protectedvoidonCreate(BundlesavedInstanceState){
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- }
- }
- <?xmlversion="1.0"encoding="utf-8"?>
- <manifestxmlns: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>
- <actionandroid:name="android.intent.action.MAIN"/>
- <categoryandroid:name="android.intent.category.LAUNCHER"/>
- </intent-filter>
- </activity>
- </application>
- </manifest>
首先是程序打开的时候,界面显示如下:
然后手指在图片上滑动,我们可以看到图片滚动的效果:
不停的翻页,页签也会跟着一起改变,下图中我们可以看到高亮显示的点是变换的:
恩,对比一下淘宝客户端的效果,我觉得我们模仿的还是挺好的。咦,好像少了点什么。。。。。。原来图片并不会自动播放。。。。。
额,好吧,那我会在下篇文章中为这个demo加入自动播放功能,不过不仅仅是自动播放功能喔,敬请期待吧,今天的文章就到这里了,有问题的朋友请在下面留言。
源码下载,请点击这里
相关推荐
在Android开发中,有时我们需要创建一个能够展示多张图片并带有页签切换功能的控件,这在电商应用中尤其常见,比如模仿淘宝的商品展示。本篇将介绍如何实现这样一个功能,主要涉及到自定义控件、图片滚动以及页签...
【Android实现图片滚动和页签控件功能的实现代码】 在Android开发中,创建一个具有图片滚动和页签功能的应用通常涉及到自定义视图控件的实现。本篇将详细介绍如何在Android中实现这一功能,主要关注核心代码的解析...
。。。...跟“Android实现图片滚动控件,含页签功能,让你的应用像淘宝一样炫起来”http://blog.csdn.net/sinyu890807/article/details/8769904效果差不多,不过自认为比他的好。呵呵 欢迎测评~~
这个项目“Android仿淘宝最新向上滚动广告条”旨在教你如何在自己的Android应用中实现类似的功能。 首先,我们需要理解滚动广告条的基本原理。滚动广告条通常采用循环播放的方式,展示一系列的广告内容,这些内容...
在Android应用开发中,"仿淘宝SKU算法选择颜色分类弹框"是一个常见的需求,它涉及到商品库存单位(Stock Keeping Unit,简称SKU)管理、用户界面设计以及交互逻辑等多个方面。这个功能通常用于电商平台,允许用户在...
在Android开发中,仿制淘宝滚动图文条是一种常见的需求,特别是在设计个人中心或者首页广告区域时。本示例代码提供了一种实现此类功能的方法。首先,我们来详细讲解一下实现这个功能的关键步骤。 1. 创建不可手动...
在Android平台上,开发一款仿淘宝界面的应用是一项挑战性的工作,涉及到UI设计、布局管理、控件使用、动画效果以及响应式交互等多个方面的技术。在这个项目中,开发者仅实现了界面部分,意味着用户可以看到与淘宝...
在Android开发领域,淘宝网客户端是一个典型的例子,它涉及到许多高级和基础的IT知识点,特别是对于移动应用开发者来说。以下是一些关键领域的详细介绍: 1. **Android SQLite编程**: Android系统内置了SQLite...
在Android应用开发中,"仿淘宝收货地址选择"是一个常见的功能需求,它涉及到用户界面设计、数据管理以及用户体验优化等多个方面。这个功能通常包括省市区三级联动选择,即用户在选择省份后,相应的市和区会根据所选...
Android淘宝客户端源码是一个深入理解移动应用开发的宝贵资源,它揭示了淘宝在Android平台上的实现细节,涵盖了众多关键技术和架构设计。本篇文章将详细探讨其中的关键知识点,帮助开发者提升对Android应用开发的...
在Android开发中,实现“仿淘宝详情页下拉放大图片”的功能是一项常见的需求,它能够为用户提供更加沉浸式的浏览体验。这个项目的核心在于图片的滚动和缩放效果,以及图片之间的平滑切换。下面我们将详细探讨这个...
淘宝UI的设计因其用户体验良好、功能丰富而闻名,因此,复刻这样一个UI可以为开发者提供深入理解Android UI设计原则、布局管理和自定义控件等知识的机会。 首先,我们来看看"说明.htm",这个文件通常会包含项目的...
【淘宝Android客户端】是基于Android技术开发的一款模拟真实淘宝应用的客户端源码,它为开发者提供了深入了解Android应用设计和实现的宝贵资源。这个项目不仅涵盖了基础的UI布局,还涉及到了复杂的系统架构和功能...
在Android应用开发中,创建一个类似淘宝或京东的详情页是一项常见的需求,它通常涉及到复杂的布局设计和交互功能。这个项目名为"DragScrollDetailsLayout-master",目标是实现一个可以嵌套ViewPager和ListView的详细...
在本项目中,"自己写的android淘宝简易客户端"是一个由个人开发者创建的Android应用程序,旨在模仿淘宝官方客户端的基础功能。这个简易客户端虽不复杂,但它包含了许多Android开发的基础知识点,对于初学者来说是一...
在Android应用开发中,图片轮播是一种常见的功能,常用于展示广告、商品图或任何需要连续滚动的视觉元素。标题“图片轮滑类似淘宝广告”所指的是,开发者已经创建了一个类似于淘宝App中的图片轮播组件,并将其封装成...
在Android开发中,ViewFlipper是一个非常有用的布局控件,它可以轻松实现动态切换多个视图的效果,例如在新闻应用中常见的广告轮播或者上下滚动的效果。在这个特定的项目中,我们模仿了淘宝和今日头条的广告展示方式...
该压缩包文件“Android高级应用源码-仿淘宝购买商品,主页后缩.zip”主要包含了一个Android应用的源代码,其设计目标是模仿淘宝网的购物功能和首页滑动效果。通过分析这个项目,我们可以深入学习Android应用开发中的...
【基于安卓高仿淘宝】项目是一个为Android Studio设计的移动端应用程序,目的是为了模拟手机淘宝的购物体验。这个项目对于学习Android应用开发的学生来说是一个很好的实践案例,它涵盖了多个关键知识点,包括用户...