Android自定義控制元件——仿淘寶、網易、彩票等廣告條、Banner的製作
最近翻看以前的某專案時,發現了一個極其常用的效果——廣告條,或者也稱不上自定義元件,但是使用頻率還是相當普遍的。
開啟市面上各大App主介面,或多或少會出現這樣的東西,甚至一個應用中出現N多個,這種展示廣告的效果,不僅動態效果好,而且眾所周知的“不佔屏”,想想在手機裝置這麼小的螢幕尺寸下,能放下幾頁甚至十幾頁的廣告迴圈播放,就知道這種廣告的使用頻率之大了。以下是我收集的部分APP中使用的效果截圖:
這些“千萬億”級別的APP都在使用的效果,為什麼我們不能效仿追隨一下呢,那下面我就開始動手做一個自己的廣告條;
要求如下:1,實現多圖展示
2,實現手勢切換
3,廣告圖片與廣告標語同時切換
4,迴圈切換,定時迴圈播放
以下是我的專案結構:
廣告條實際上用的是ViewPager來做的,佈局中僅僅放了一個ViewPager而已,其它的圖片切換都是用ViewPager來展示的,佈局如下:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" > <android.support.v4.view.ViewPager android:id="@+id/viewpager" android:layout_width="match_parent" android:layout_height="200dp" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignBottom="@id/viewpager" android:background="#33000000" android:orientation="vertical" android:padding="5dp" > <TextView android:id="@+id/tv_image_description" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:text="鞏俐不低俗,我們也不低俗" android:textColor="@android:color/white" /> <LinearLayout android:id="@+id/ll_point_group" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:layout_marginTop="5dp" android:orientation="horizontal" > </LinearLayout> </LinearLayout> </RelativeLayout>
上面的ViewPager用來顯示廣告圖片,下面的LinearLayout嵌在ViewPager底部,實現陰影效果。裡面包括TextView來顯示廣告標語,和一個LinearLayout來顯示廣告切換狀態指示點。寫完佈局,就可以為這個ViewPager載入資料,增加動態效果了,主要程式碼如下,註釋清晰:
package com.example.banner; import java.util.ArrayList; import java.util.List; import android.os.Bundle; import android.os.SystemClock; import android.support.v4.view.PagerAdapter; import android.support.v4.view.ViewPager; import android.support.v4.view.ViewPager.OnPageChangeListener; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import android.widget.LinearLayout.LayoutParams; import android.app.Activity; public class MainActivity extends Activity { private List<ImageView> mImageList; /** 廣告條正下方的標語 */ private String[] imageDescriptionArray = { // "鞏俐不低俗,我就不能低俗", // "撲樹又回來啦!再唱經典老歌引萬人大合唱", // "揭祕北京電影如何升級", // "樂視網TV版大派送", // "熱血屌絲的反殺" }; /** 記錄上一次點的位置,預設為0 */ private int previousPointEnale = 0; private ViewPager mViewPager; private LinearLayout llPointGroup; private TextView tvDescription; /** 記錄是否停止迴圈播放 */ private boolean isStop = false; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); init(); // 開啟子執行緒,讓廣告條以2秒的頻率迴圈播放 new Thread(new Runnable() { @Override public void run() { while (!isStop) { SystemClock.sleep(2000); runOnUiThread(new Runnable() { public void run() { mViewPager.setCurrentItem(mViewPager.getCurrentItem() + 1); } }); } } }).start(); } private void init() { llPointGroup = (LinearLayout) findViewById(R.id.ll_point_group); tvDescription = (TextView) findViewById(R.id.tv_image_description); mImageList = new ArrayList<ImageView>(); int[] imageIds = new int[] { R.drawable.a, R.drawable.b, R.drawable.c, R.drawable.d, R.drawable.e }; ImageView mImageView; LayoutParams params; // 初始化廣告條資源 for (int id : imageIds) { mImageView = new ImageView(this); mImageView.setBackgroundResource(id); mImageList.add(mImageView); // 初始化廣告條正下方的"點" View dot = new View(this); dot.setBackgroundResource(R.drawable.point_background); params = new LayoutParams(5, 5); params.leftMargin = 10; dot.setLayoutParams(params); dot.setEnabled(false); llPointGroup.addView(dot); } mViewPager = (ViewPager) findViewById(R.id.viewpager); mViewPager.setAdapter(new MyAdapter()); // 設定廣告條跳轉時,廣告語和狀態語的變化 mViewPager.setOnPageChangeListener(new MyListener()); // 初始化廣告條,當前索引Integer.MAX_VALUE的一半 int index = (Integer.MAX_VALUE / 2) - (Integer.MAX_VALUE / 2 % mImageList.size()); mViewPager.setCurrentItem(index); // 設定當前選中的Page,會觸發onPageChangListener.onPageSelected方法 } private class MyListener implements OnPageChangeListener { @Override public void onPageScrollStateChanged(int arg0) { // TODO Auto-generated method stub } @Override public void onPageScrolled(int arg0, float arg1, int arg2) { // TODO Auto-generated method stub } @Override public void onPageSelected(int arg0) { // 獲取新的位置 int newPosition = arg0 % imageDescriptionArray.length; // 設定廣告標語 tvDescription.setText(imageDescriptionArray[newPosition]); // 消除上一次的狀態點 llPointGroup.getChildAt(previousPointEnale).setEnabled(false); // 設定當前的狀態點“點” llPointGroup.getChildAt(newPosition).setEnabled(true); // 記錄位置 previousPointEnale = newPosition; } } /** * ViewPager資料介面卡 */ private class MyAdapter extends PagerAdapter { @Override public int getCount() { // 將viewpager頁數設定成Integer.MAX_VALUE,可以模擬無限迴圈 return Integer.MAX_VALUE; } /** * 複用物件 true 複用view false 複用的是Object */ @Override public boolean isViewFromObject(View arg0, Object arg1) { // TODO Auto-generated method stub return arg0 == arg1; } /** * 銷燬物件 * * @param position * 被銷燬物件的索引位置 */ @Override public void destroyItem(ViewGroup container, int position, Object object) { container.removeView(mImageList.get(position % mImageList.size())); } /** * 初始化一個物件 * * @param position * 初始化物件的索引位置 */ @Override public Object instantiateItem(ViewGroup container, int position) { container.addView(mImageList.get(position % mImageList.size())); return mImageList.get(position % mImageList.size()); } } @Override protected void onDestroy() { // activity銷燬時候,關閉迴圈播放 isStop = true; super.onDestroy(); } }
需要注意的是,為了達到廣告條迴圈播放的效果,故不能將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
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval" >
<corners android:radius="0.5dp" />
<solid android:color="#AAFFFFFF" />
</shape>
廣告條普通樣式:point_bg_normal.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval" >
<corners android:radius="0.5dp" />
<solid android:color="#55000000" />
</shape>
廣告條的狀態選擇器:point_background.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/point_bg_enable" android:state_enabled="true"></item>
<item android:drawable="@drawable/point_bg_normal" android:state_enabled="false"></item>
</selector>
以下是效果圖:
最後,還需要實現廣告的自動迴圈播放,這個很簡單,只要開啟一個新執行緒,線上程中每隔2000ms迴圈更新一下ViewPager就行。就是在ViewPager中獲取當前展示的Item的索引,加上1之後,設定展示這個值即可。還得注意程式的嚴謹性啊,當activity銷燬的時候,這個新執行緒裡負責迴圈播放的程式碼是徐璈停止執行的。故設定一個boolean的變數isStop,在while迴圈的時候,判斷是否開啟/關閉,在activity的onDestory方法中,設定其為true,即停止迴圈播放!