Android自定义控件——仿淘宝、网易、彩票等广告条、Banner的制作

来源:互联网 发布:林彪为什么出逃 知乎 编辑:程序博客网 时间:2024/05/16 11:41

该文章出自 http://blog.csdn.net/allen315410/article/details/39294343


最近翻看以前的某项目时,发现了一个极其常用的效果——广告条,或者也称不上自定义组件,但是使用频率还是相当普遍的。

打开市面上各大App主界面,或多或少会出现这样的东西,甚至一个应用中出现N多个,这种展示广告的效果,不仅动态效果好,而且众所周知的“不占屏”,想想在手机设备这么小的屏幕尺寸下,能放下几页甚至十几页的广告循环播放,就知道这种广告的使用频率之大了。以下是我收集的部分APP中使用的效果截图:

这些“千万亿”级别的APP都在使用的效果,为什么我们不能效仿追随一下呢,那下面我就开始动手做一个自己的广告条;

要求如下:1,实现多图展示

                    2,实现手势切换

                    3,广告图片与广告标语同时切换

                   4,循环切换,定时循环播放

以下是我的项目结构:

广告条实际上用的是ViewPager来做的,布局中仅仅放了一个ViewPager而已,其它的图片切换都是用ViewPager来展示的,布局如下:

[html] view plain copy
 print?
  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     xmlns:tools="http://schemas.android.com/tools"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent" >  
  5.   
  6.     <android.support.v4.view.ViewPager  
  7.         android:id="@+id/viewpager"  
  8.         android:layout_width="match_parent"  
  9.         android:layout_height="200dp" />  
  10.   
  11.     <LinearLayout  
  12.         android:layout_width="match_parent"  
  13.         android:layout_height="wrap_content"  
  14.         android:layout_alignBottom="@id/viewpager"  
  15.         android:background="#33000000"  
  16.         android:orientation="vertical"  
  17.         android:padding="5dp" >  
  18.   
  19.         <TextView  
  20.             android:id="@+id/tv_image_description"  
  21.             android:layout_width="wrap_content"  
  22.             android:layout_height="wrap_content"  
  23.             android:layout_gravity="center_horizontal"  
  24.             android:text="巩俐不低俗,我们也不低俗"  
  25.             android:textColor="@android:color/white" />  
  26.   
  27.         <LinearLayout  
  28.             android:id="@+id/ll_point_group"  
  29.             android:layout_width="wrap_content"  
  30.             android:layout_height="wrap_content"  
  31.             android:layout_gravity="center_horizontal"  
  32.             android:layout_marginTop="5dp"  
  33.             android:orientation="horizontal" >  
  34.         </LinearLayout>  
  35.     </LinearLayout>  
  36.   
  37. </RelativeLayout>  

上面的ViewPager用来显示广告图片,下面的LinearLayout嵌在ViewPager底部,实现阴影效果。里面包括TextView来显示广告标语,和一个LinearLayout来显示广告切换状态指示点。写完布局,就可以为这个ViewPager加载数据,增加动态效果了,主要代码如下,注释清晰:

[java] view plain copy
 print?
  1. package com.example.banner;  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.List;  
  5.   
  6. import android.os.Bundle;  
  7. import android.os.SystemClock;  
  8. import android.support.v4.view.PagerAdapter;  
  9. import android.support.v4.view.ViewPager;  
  10. import android.support.v4.view.ViewPager.OnPageChangeListener;  
  11. import android.view.View;  
  12. import android.view.ViewGroup;  
  13. import android.widget.ImageView;  
  14. import android.widget.LinearLayout;  
  15. import android.widget.TextView;  
  16. import android.widget.LinearLayout.LayoutParams;  
  17. import android.app.Activity;  
  18.   
  19. public class MainActivity extends Activity {  
  20.   
  21.     private List<ImageView> mImageList;  
  22.     /** 广告条正下方的标语 */  
  23.     private String[] imageDescriptionArray = { //  
  24.     "巩俐不低俗,我就不能低俗"//  
  25.             "扑树又回来啦!再唱经典老歌引万人大合唱"//  
  26.             "揭秘北京电影如何升级"//  
  27.             "乐视网TV版大派送"//  
  28.             "热血屌丝的反杀" };  
  29.     /** 记录上一次点的位置,默认为0 */  
  30.     private int previousPointEnale = 0;  
  31.     private ViewPager mViewPager;  
  32.     private LinearLayout llPointGroup;  
  33.     private TextView tvDescription;  
  34.     /** 记录是否停止循环播放 */  
  35.     private boolean isStop = false;  
  36.   
  37.     @Override  
  38.     protected void onCreate(Bundle savedInstanceState) {  
  39.         super.onCreate(savedInstanceState);  
  40.         setContentView(R.layout.activity_main);  
  41.   
  42.         init();  
  43.         // 开启子线程,让广告条以2秒的频率循环播放  
  44.         new Thread(new Runnable() {  
  45.   
  46.             @Override  
  47.             public void run() {  
  48.   
  49.                 while (!isStop) {  
  50.                     SystemClock.sleep(2000);  
  51.                     runOnUiThread(new Runnable() {  
  52.                         public void run() {  
  53.                             mViewPager.setCurrentItem(mViewPager.getCurrentItem() + 1);  
  54.                         }  
  55.                     });  
  56.                 }  
  57.             }  
  58.         }).start();  
  59.     }  
  60.   
  61.     private void init() {  
  62.         llPointGroup = (LinearLayout) findViewById(R.id.ll_point_group);  
  63.         tvDescription = (TextView) findViewById(R.id.tv_image_description);  
  64.         mImageList = new ArrayList<ImageView>();  
  65.         int[] imageIds = new int[] { R.drawable.a, R.drawable.b, R.drawable.c, R.drawable.d, R.drawable.e };  
  66.         ImageView mImageView;  
  67.         LayoutParams params;  
  68.         // 初始化广告条资源  
  69.         for (int id : imageIds) {  
  70.             mImageView = new ImageView(this);  
  71.             mImageView.setBackgroundResource(id);  
  72.             mImageList.add(mImageView);  
  73.   
  74.             // 初始化广告条正下方的"点"  
  75.             View dot = new View(this);  
  76.             dot.setBackgroundResource(R.drawable.point_background);  
  77.             params = new LayoutParams(55);  
  78.             params.leftMargin = 10;  
  79.             dot.setLayoutParams(params);  
  80.             dot.setEnabled(false);  
  81.             llPointGroup.addView(dot);  
  82.         }  
  83.         mViewPager = (ViewPager) findViewById(R.id.viewpager);  
  84.         mViewPager.setAdapter(new MyAdapter());  
  85.   
  86.         // 设置广告条跳转时,广告语和状态语的变化  
  87.         mViewPager.setOnPageChangeListener(new MyListener());  
  88.   
  89.         // 初始化广告条,当前索引Integer.MAX_VALUE的一半  
  90.         int index = (Integer.MAX_VALUE / 2) - (Integer.MAX_VALUE / 2 % mImageList.size());  
  91.         mViewPager.setCurrentItem(index); // 设置当前选中的Page,会触发onPageChangListener.onPageSelected方法  
  92.     }  
  93.   
  94.     private class MyListener implements OnPageChangeListener {  
  95.   
  96.         @Override  
  97.         public void onPageScrollStateChanged(int arg0) {  
  98.             // TODO Auto-generated method stub  
  99.   
  100.         }  
  101.   
  102.         @Override  
  103.         public void onPageScrolled(int arg0, float arg1, int arg2) {  
  104.             // TODO Auto-generated method stub  
  105.   
  106.         }  
  107.   
  108.         @Override  
  109.         public void onPageSelected(int arg0) {  
  110.             // 获取新的位置  
  111.             int newPosition = arg0 % imageDescriptionArray.length;  
  112.             // 设置广告标语  
  113.             tvDescription.setText(imageDescriptionArray[newPosition]);  
  114.             // 消除上一次的状态点  
  115.             llPointGroup.getChildAt(previousPointEnale).setEnabled(false);  
  116.             // 设置当前的状态点“点”  
  117.             llPointGroup.getChildAt(newPosition).setEnabled(true);  
  118.             // 记录位置  
  119.             previousPointEnale = newPosition;  
  120.         }  
  121.   
  122.     }  
  123.   
  124.     /** 
  125.      * ViewPager数据适配器 
  126.      */  
  127.     private class MyAdapter extends PagerAdapter {  
  128.   
  129.         @Override  
  130.         public int getCount() {  
  131.             // 将viewpager页数设置成Integer.MAX_VALUE,可以模拟无限循环  
  132.             return Integer.MAX_VALUE;  
  133.         }  
  134.   
  135.         /** 
  136.          * 复用对象 true 复用view false 复用的是Object 
  137.          */  
  138.         @Override  
  139.         public boolean isViewFromObject(View arg0, Object arg1) {  
  140.             // TODO Auto-generated method stub  
  141.             return arg0 == arg1;  
  142.         }  
  143.   
  144.         /** 
  145.          * 销毁对象 
  146.          *  
  147.          * @param position 
  148.          *            被销毁对象的索引位置 
  149.          */  
  150.         @Override  
  151.         public void destroyItem(ViewGroup container, int position, Object object) {  
  152.             container.removeView(mImageList.get(position % mImageList.size()));  
  153.         }  
  154.   
  155.         /** 
  156.          * 初始化一个对象 
  157.          *  
  158.          * @param position 
  159.          *            初始化对象的索引位置 
  160.          */  
  161.         @Override  
  162.         public Object instantiateItem(ViewGroup container, int position) {  
  163.             container.addView(mImageList.get(position % mImageList.size()));  
  164.             return mImageList.get(position % mImageList.size());  
  165.         }  
  166.   
  167.     }  
  168.   
  169.     @Override  
  170.     protected void onDestroy() {  
  171.         // activity销毁时候,关闭循环播放  
  172.         isStop = true;  
  173.         super.onDestroy();  
  174.     }  
  175.   
  176. }  

         需要注意的是,为了达到广告条循环播放的效果,故不能将ViewPager所展示的总数设置较小的定值,这样若ViewPager划过这个定值的时候,页面会定住,用户体验就不太理想化了,为了能达到这个循环的效果,只能将ViewPager展示总数设置成一个很大的值,以便来给用户造成无限循环的假象。那么这个值该取多大合适呢?思前想后,觉得在PagerAdapter的getCount方法中,返回Integer.MAX_VALUE这个值,这个值2147483647,无论如何用户也不可以拿手机没事划上个好几亿次吧。

       到此还要注意的地方就是,因为getCount中返回Integer.MAX_VALUE这么大数值,为了达到有图循环的效果,避免Bug,所以其后每次涉及到position索引的地方都得用position和资源尺度取余的结果。

      此外,在“点”的初始化的时候,应当设置“点”的索引为int index = (Integer.MAX_VALUE / 2) - (Integer.MAX_VALUE / 2 % mImageList.size());

而不能简单设置成0,若是设置成0,就无法制造出循环播放的“假象”,不信试试设置0,往左滑动。

关于“点”的资源,没有用到图片,下面是资源代码,贴出来:

广告条获得焦点:point_bg_enable.xml

[html] view plain copy
 print?
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <shape xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:shape="oval" >  
  4.   
  5.     <corners android:radius="0.5dp" />  
  6.   
  7.     <solid android:color="#AAFFFFFF" />  
  8.   
  9. </shape>  

广告条普通样式:point_bg_normal.xml

[html] view plain copy
 print?
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <shape xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:shape="oval" >  
  4.   
  5.     <corners android:radius="0.5dp" />  
  6.   
  7.     <solid android:color="#55000000" />  
  8.   
  9. </shape>  

广告条的状态选择器:point_background.xml

[html] view plain copy
 print?
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <selector xmlns:android="http://schemas.android.com/apk/res/android">  
  3.   
  4.     <item android:drawable="@drawable/point_bg_enable" android:state_enabled="true"></item>  
  5.     <item android:drawable="@drawable/point_bg_normal" android:state_enabled="false"></item>  
  6.   
  7. </selector>  
以下是效果图:

最后,还需要实现广告的自动循环播放,这个很简单,只要开启一个新线程,在线程中每隔2000ms循环更新一下ViewPager就行。就是在ViewPager中获取当前展示的Item的索引,加上1之后,设置展示这个值即可。还得注意程序的严谨性啊,当activity销毁的时候,这个新线程里负责循环播放的代码是徐璈停止执行的。故设置一个boolean的变量isStop,在while循环的时候,判断是否开启/关闭,在activity的onDestory方法中,设置其为true,即停止循环播放!


源码请在这里下载

0 0
原创粉丝点击