自定義Banner輪播廣告(真*無限迴圈無卡頓&設定切換速度)
阿新 • • 發佈:2019-01-29
前言
Github上有很多輪播廣告的原始碼,比如帶著很酷炫動畫的flashView框架。
不過就學習而已,我建議每個人都應該自己多嘗試著寫一些控制元件。
以下,是我為小白們分享的簡單經驗。
自定義控制元件
先展示下效果圖。!由於不會做gif圖,只能粗略的展示效果,看官見諒 ![這裡寫圖片描述](https://img-blog.csdn.net/20170225165425535?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZGFiZW5kYW4wNzE0/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
**簡單說下思路,就是由一個展示圖片的viewPager和右下角的三個radioButton組成的自定義組合控制元件。**
送上第一道菜——佈局程式碼
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height ="match_parent">
<android.support.v4.view.ViewPager
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent">
</android.support.v4.view.ViewPager>
<RadioGroup
android:id="@+id/radio_group"
android:layout_alignParentBottom ="true"
android:layout_alignParentRight="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginRight="15dp"
android:layout_marginBottom="15dp"
>
<RadioButton
android:id="@+id/rb1"
android:layout_width="7dp"
android:layout_height="8dp"
android:button="@null"
android:background="@drawable/circle_advertisement"
android:clickable="false"
/>
<RadioButton
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:id="@+id/rb2"
android:layout_width="7dp"
android:layout_height="8dp"
android:button="@null"
android:background="@drawable/circle_advertisement"
android:clickable="false"
/>
<RadioButton
android:id="@+id/rb3"
android:layout_width="7dp"
android:layout_height="8dp"
android:button="@null"
android:background="@drawable/circle_advertisement"
android:clickable="false"
/>
</RadioGroup>
</RelativeLayout>
簡單說一說自定義組合控制元件的使用,其實很簡單。
1.讓你的控制元件繼承一個佈局(LinerLayout or RelativeLayout)
public class YAdvertisementLayout extends LinearLayout
2.重寫構造方法,新增你要進行的操作(構造方法有4個,分別對應不同構造方式),
儘量在每個構造方法下都要寫你初始化的操作。
public YAdvertisementLayout(Context context) {
super(context);
initView(context);
}
public YAdvertisementLayout(Context context, AttributeSet attrs) {
super(context, attrs);
initView(context);
}
public YAdvertisementLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView(context);
}
3.初始化佈局,組合控制元件就加上如下程式碼,然後就可以對你的控制元件為所欲為了
View v = inflate(context, R.layout.widget_advertisement, this);
接下來的步驟,就是正常findViewById。初始化資料來源,對pager進行設定廣告圖片。
進入今天的重頭戲,優化Banner圖
1.如何使你的輪播廣告無限迴圈
2. 手動設定viewPager切換速度
第一部分:viewPager無限迴圈
這個部分得先從viewPager的介面卡PagerAdapter講起
簡單介紹下PagerAdapter的各個方法
public int getCount();//獲取viewPager資料來源總個數
public boolean isViewFromObject(View view, Object object);//這個方法是判斷物件是否相同,相同直接複用,所以一般都是return view == object;
//還有兩個方法是要自己重寫的
public void destroyItem(ViewGroup container, int position, Object object);//顧名思義,移除物件的操作container.removeView(list.get(position));
public Object instantiateItem(ViewGroup container, int position);//重點方法,在此方法進行初始化操作,設定圖片,設定點選時間等等。
舉個栗子
//list是資料來源
public class MyAdapter extends PagerAdapter {
@Override
public int getCount() {
return list.size();
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
// container.removeView(list.get(position%list.size()));
container.removeView(list.get(position));
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
container.addView(list.get(position));
list.get(position).setImageResource(imgs.get(position));
list.get(position).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(context, "我被點選了", Toast.LENGTH_SHORT).show();
}
});
return list.get(position);
}
}
這個就是一般流程。之前有一種無限迴圈的方式,其實個人認為又low又搞笑。
在getCount()方法中return Integer.MAX_VALUE;
相當於建立無數物件,在初始化物件時container.addView(list.get(position%list.size()));
如此一來,反覆建立物件,缺點顯而易見。
那麼問題來了,如何能優雅的裝逼...
**上程式碼!**
/**新增首末圖,末圖與首圖相同,首圖為末圖*/
imgs.add(R.drawable.match_banner);
imgs.add(R.drawable.advertisement_2);
imgs.add(R.drawable.advertisement_3);
imgs.add(0,R.drawable.advertisement_3);
imgs.add(list.size()-1.drawable.match_banner);
相信看到這裡,你應該有了一些想法。
假設三張動圖,我們要支援無限滑動,那就設定3+2資料來源,編號0| 123 |4(123為展示圖,4圖與1圖相同,
這樣做是為了讓viewPager支援左右滑動,並且欺騙使用者的眼睛)在滑動到4時,
我們進行一個無動畫切換mPager.setCurrentItem(1,false);同理滑動到0時無動畫切換3.mPager.setCurrentItem(3,false)
註釋:在滑動到資料來源末端時,viewPager是不支援繼續滑動的
原理就是這麼簡單,但是,僅僅這樣會有一個bug,是在首末頁切換時,會出現,動畫並不夠平滑,有一種卡頓的感覺。
解決方法:
mPager.addOnPageChangeListener(this);
//在這個方法上做文章,positionOffset這是偏移比例,positionOffsetPixels這是偏移畫素
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
//原理就是在視覺上滑動完全結束了,進行無動畫跳轉,如此一來切換過程就沒有卡頓效果
if (position == 0 && positionOffset == 0)
mPager .setCurrentItem(list.size() - 2, false);
else if (position == list.size() - 1 && positionOffset == 0)
mPager .setCurrentItem(1, false);
}
這也是為什麼不在
public void onPageSelected(int position) ;方法上進行跳轉的原因
第二部分:手動設定viewPager切換速度
這個部分設計到反射,修改原始碼設定時間。本人就不丟人現眼了,直接上原始碼
//新增工具類
import java.lang.reflect.Field;
import android.content.Context;
import android.support.v4.view.ViewPager;
import android.view.animation.Interpolator;
import android.widget.Scroller;
/**
* ViewPager 滾動速度設定
*
* @author lyy
*
*/
public class ViewPagerScroller extends Scroller {
private int mScrollDuration = 2000; // 滑動速度
/**
* 設定速度速度
*
* @param duration
*/
public void setScrollDuration(int duration) {
this.mScrollDuration = duration;
}
public ViewPagerScroller(Context context) {
super(context);
}
public ViewPagerScroller(Context context, Interpolator interpolator) {
super(context, interpolator);
}
public ViewPagerScroller(Context context, Interpolator interpolator,
boolean flywheel) {
super(context, interpolator, flywheel);
}
@Override
public void startScroll(int startX, int startY, int dx, int dy, int duration) {
super.startScroll(startX, startY, dx, dy, mScrollDuration);
}
@Override
public void startScroll(int startX, int startY, int dx, int dy) {
super.startScroll(startX, startY, dx, dy, mScrollDuration);
}
public void initViewPagerScroll(ViewPager viewPager) {
try {
Field mScroller = ViewPager.class.getDeclaredField("mScroller");
mScroller.setAccessible(true);
mScroller.set(viewPager, this);
} catch (Exception e) {
e.printStackTrace();
}
}
}
//使用方式
private void fixSpeed() {
ViewPagerScroller pagerScroller = new ViewPagerScroller(mPager.getContext());
pagerScroller.setScrollDuration(1000*1);//設定時間,時間越長,速度越慢
pagerScroller.initViewPagerScroll(mPager);
}
原始碼
佈局檔案,文章開始已經給出了主要佈局,下面附上radioButton圖片
<?xml version="1.0" encoding="UTF-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/circle_advertisement_normal" android:state_checked="false"/>
<item android:drawable="@drawable/circle_advertisement_checked" android:state_checked="true"/>
</selector>
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval"
android:useLevel="false">
<solid android:color="#ffffff"/>
<size android:width="10dp"
android:height="10dp"/>
</shape>
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval"
android:useLevel="false">
<solid android:color="#80ffffff"/>
<size android:width="10dp"
android:height="10dp"/>
</shape>
控制元件原始碼
package app.ui.widget;
import android.content.Context;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import com.wy.sport.R;
import java.util.ArrayList;
import java.util.List;
import android.os.Handler;
/**
* Created by Yangmu on 2017/2/21.
*/
public class YAdvertisementLayout extends LinearLayout implements ViewPager.OnPageChangeListener {
private List<ImageView> list = new ArrayList<>();
private List<Integer> imgs = new ArrayList<>();
private Context context;
private ViewPager mPager;
private RadioButton mRb1;
private RadioButton mRb2;
private RadioButton mRb3;
private RadioGroup mRadioGroup;
private static final String TAG = "ym";
private Handler handler;
private Runnable r;
private MyAdapter adpter;
public YAdvertisementLayout(Context context) {
super(context);
initView(context);
}
public YAdvertisementLayout(Context context, AttributeSet attrs) {
super(context, attrs);
initView(context);
}
public YAdvertisementLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView(context);
}
private void initView(Context context) {
this.context = context;
View v = inflate(context, R.layout.widget_advertisement, this);
mPager = (ViewPager) v.findViewById(R.id.pager);
mRadioGroup = (RadioGroup) v.findViewById(R.id.radio_group);
mRb1 = (RadioButton) v.findViewById(R.id.rb1);
mRb2 = (RadioButton) v.findViewById(R.id.rb2);
mRb3 = (RadioButton) v.findViewById(R.id.rb3);
initPager();
mPager.addOnPageChangeListener(this);
mPager.setCurrentItem(1);
// start();
fixSpeed();
}
/**修改動畫播放速度*/
private void fixSpeed() {
ViewPagerScroller pagerScroller = new ViewPagerScroller(mPager.getContext());
pagerScroller.setScrollDuration(1000*1);//設定時間,時間越長,速度越慢
pagerScroller.initViewPagerScroll(mPager);
}
//開啟自動跳轉廣告功能
public void start() {
handler = new Handler();
r = new Runnable() {
@Override
public void run() {
mPager.setCurrentItem(mPager.getCurrentItem()+1);
// handler.postDelayed(r,1000*3);
}
};
handler.postDelayed(r,1000*4);
}
private void initPager() {
for (int i = 0; i < 5; i++) {
ImageView iv1 = new ImageView(context);
iv1.setScaleType(ImageView.ScaleType.FIT_XY);
list.add(iv1);
}
Log.e(TAG, "initPager: "+list.size() );
//
/**新增首末圖,末圖與首圖相同,首圖為末圖*/
imgs.add(R.drawable.advertisement_3);
imgs.add(R.drawable.match_banner);
imgs.add(R.drawable.advertisement_2);
imgs.add(R.drawable.advertisement_3);
imgs.add(R.drawable.match_banner);
adpter = new MyAdapter();
mPager.setAdapter(adpter);
mRadioGroup.check(mRb1.getId());
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
Log.e(TAG, "onPageScrolled: position"+position+" positionOffset"+positionOffset+" positionOffsetPixels"+positionOffsetPixels );
if (position == 0 && positionOffset == 0)
mPager .setCurrentItem(list.size() - 2, false);
else if (position == list.size() - 1 && positionOffset == 0)
mPager .setCurrentItem(1, false);
}
@Override
public void onPageSelected(int position) {
if (list.size() > 3) { //多於1,才會迴圈跳轉
switch (position) {
case 0:
mRadioGroup.check(mRb3.getId());
case 1:
mRadioGroup.check(mRb1.getId());
break;
case 2:
mRadioGroup.check(mRb2.getId());
break;
case 3:
mRadioGroup.check(mRb3.getId());
break;
case 4:
mRadioGroup.check(mRb1.getId());
}
}else {
mPager.setCurrentItem(1, false);
}
}
@Override
public void onPageScrollStateChanged(int state) {
switch(state){
case 1: //滑動狀態
handler.removeCallbacks(r);
break;
case 2://滑動結束狀態
handler.postDelayed(r,1000*4);
break;
default:
break;
}
}
public class MyAdapter extends PagerAdapter {
@Override
public int getCount() {
return list.size();
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
// container.removeView(list.get(position%list.size()));
container.removeView(list.get(position));
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
container.addView(list.get(position));
Log.e(TAG, "instantiateItem: position"+position );
list.get(position).setImageResource(imgs.get(position));
list.get(position).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// Toast.makeText(context, "呵呵", Toast.LENGTH_SHORT).show();
}
});
return list.get(position);
}
}
/**對外借口,通過此方法修改圖片*/
public void changerImages(int imgs[]){
this.imgs.clear();
for (int i = 0; i < imgs.length; i++) {
this.imgs.add(imgs[i]);
}
this.imgs.add(0,imgs[imgs.length-1]);
this.imgs.add(this.imgs.size()-1,imgs[0]);
Log.e(TAG, "changerImages: 更新完畢" );
adpter.notifyDataSetChanged();
}
}