上一篇博客寫了類型QQ看圖片的輪播功能,其實(shí)有一點(diǎn)沒有講到,Android使用序列化接口Parcelable忌傻、Serializable.這個(gè)抽空更新下博客;
今天的主題是viewpage輪播圖,老生長談了,先來看下效果圖吧!
viewpager實(shí)現(xiàn)輪播圖有兩種思路,我這里主要用的思路是
原來的處理方式:圖1兆蕉,圖2体箕,圖3
現(xiàn)在的處理方式:新圖3姜凄、圖1、圖2暖璧、圖3弄喘、新圖1。
這樣的思路實(shí)現(xiàn)起來是很方便的;
來看具體實(shí)現(xiàn)的代碼吧!
布局文件 其實(shí)就簡單viewpage和 小滑點(diǎn)LinearLayout
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="200dp"
tools:context="com.cs.test_rx3.MainActivity">
<android.support.v4.view.ViewPager
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
<LinearLayout
android:id="@+id/ll_ponit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_gravity="bottom"
android:paddingBottom="12dp"
android:gravity="center"
android:layout_alignParentBottom="true"
>
</LinearLayout>
</RelativeLayout>```
定制viewpage之前需要去設(shè)置適配器,這個(gè)方法是載入布局的時(shí)候需要用到
``` @Override
public Object instantiateItem(ViewGroup container, final int position) {
View view=mViews.get(position);
if (myViewListenner != null){
view.setOnClickListener(new View.OnClickListener() {
// 處理極端情況贡蓖,此情況出現(xiàn)在輪播最后一張圖切換到第一張圖曹鸠,ViewPaper實(shí)現(xiàn)輪播原理決定的。
//因?yàn)関iew層次和數(shù)據(jù)層次進(jìn)行分開處理了,所有需要額外寫下
@Override
public void onClick(View view) {
vPonsiton = position;
// 如果不寫size!=0會(huì)報(bào)異常java.lang.ArithmeticException: divide by zero size為0的情況下
if (vPonsiton >size && size!=0) {
vPonsiton = vPonsiton % size;
}
myViewListenner.onItemClick(vPonsiton);
}
});
}
container.addView(view);
return view;
}
*主代碼文件
public class MainActivity extends AppCompatActivity {
private ViewPager viewpager;
// 默認(rèn)輪播時(shí)間
private int delay = 3000;
private MyAdapter myAdapter;
// 輪播當(dāng)前位置
private int mCurrentPosition = 0;
private LinearLayout ponit;
private String[] Imgs = {
"http://ww3.sinaimg.cn/large/610dc034jw1f9em0sj3yvj20u00w4acj.jpg",
"http://ww3.sinaimg.cn/large/610dc034jw1f9nuk0nvrdj20u011haci.jpg",
"http://ww3.sinaimg.cn/large/610dc034jw1f9rc3qcfm1j20u011hmyk.jpg",
"http://ww3.sinaimg.cn/large/610dc034jw1f9em0sj3yvj20u00w4acj.jpg"
};
private int size;
private List<View> mViews = new ArrayList<>();
private ImageView imageView;
private View view;
private Subscription subscribe;
private ImageView[] mIndicators;
private int ponitpos;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
Log.d("TAG", size + "");
}
/**
* 加載views
* 加載url數(shù)據(jù),
* 綁定
* 偷天換日
*/
private void initView() {
viewpager = (ViewPager) findViewById(R.id.viewpager);
ponit = (LinearLayout) findViewById(R.id.ll_ponit);
myAdapter = new MyAdapter(this, mViews, size);
viewpager.setAdapter(myAdapter);
mViews.clear();
//載入圖片的時(shí)候需要用的item,默認(rèn)用網(wǎng)絡(luò)圖片進(jìn)行加載
view = LayoutInflater.from(this).inflate(R.layout.item, null);
imageView = (ImageView) view.findViewById(R.id.image);
Glide.with(this)
.load(Imgs[Imgs.length - 1])
.centerCrop()
.into(imageView);
mViews.add(view);
for (int i = 0; i < Imgs.length; i++) {
view = LayoutInflater.from(this).inflate(R.layout.item, null);
imageView = (ImageView) view.findViewById(R.id.image);
Glide.with(this)
.load(Imgs[i])
.centerCrop()
.into(imageView);
mViews.add(view);
}
view = LayoutInflater.from(this).inflate(R.layout.item, null);
imageView = (ImageView) view.findViewById(R.id.image);
Glide.with(this)
.load(Imgs[0])
.centerCrop()
.into(imageView);
mViews.add(view);
myAdapter.notifyDataSetChanged();
//加入小圓點(diǎn)
size = Imgs.length;
mIndicators = new ImageView[size];
for (int i = 0; i < size; i++) {
mIndicators[i] = new ImageView(this);
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(10,10);
lp.setMargins(10, 0, 10, 0);
mIndicators[i].setLayoutParams(lp);
mIndicators[i].setBackgroundResource(R.drawable.selector_home_banner_points);
ponit.addView(mIndicators[i]);
}
//進(jìn)行監(jiān)聽
viewpager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
int max = mViews.size() - 1;
mCurrentPosition = position;
if (position == 0) {
mCurrentPosition = max - 1;
} else if (position == max) {
mCurrentPosition = 1;
}
ponitpos = mCurrentPosition - 1;
int c = viewpager.getCurrentItem();
Log.e("c",c+"");
for (int i = 0; i <mViews.size(); i++) {
if(c==0||c==mViews.size()-1){
break;
}else{
c = c-1;
break;
}
}
/*switch (c){
case 0:
break;
case 1:
c =0;
break;
case 2:
c = 1;
break;
case 3:
c =2;
break;
case 4:
break;
}*/
Log.e("c",c+"");
// TODO: 2016/11/17 這里實(shí)現(xiàn)的方式有兩種,從不同思路去實(shí)現(xiàn),有興趣可以參考以上代碼,新手會(huì)遇到坑
for (int i = 0; i < mIndicators.length; i++) {
//mIndicators[i].setSelected(c==i);
mIndicators[i].setSelected(ponitpos==i);
}
}
@Override
public void onPageScrollStateChanged(int state) {
if (state == 0) {
viewpager.setCurrentItem(mCurrentPosition, false);
}
}
});
//調(diào)用rxjava來執(zhí)行
subscribe = Observable.interval(delay, TimeUnit.MILLISECONDS)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Long>() {
@Override
public void call(Long aLong) {
mCurrentPosition++;
if (viewpager != null)
viewpager.setCurrentItem(mCurrentPosition, false);
}
});
viewpager.setCurrentItem(1, false);
// 初始化指示器圖片集合
myAdapter.setMyViewListenner(new MyAdapter.MyViewListenner() {
@Override
public void onItemClick(int possiton) {
Toast.makeText(MainActivity.this, "你點(diǎn)擊了第" + possiton + "個(gè)", Toast.LENGTH_SHORT).show();
}
});
}
// @Override
public boolean dispatchTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_DOWN:
// 手指按下或者滑動(dòng)的過程中停止輪播
// 手指按下或者滑動(dòng)的過程中停止輪播
subscribe.unsubscribe();
break;
case MotionEvent.ACTION_UP:
// 手指離開屏幕開始輪播
subscribe = Observable.interval(delay, TimeUnit.MILLISECONDS)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Long>() {
@Override
public void call(Long aLong) {
mCurrentPosition++;
if (viewpager != null)
viewpager.setCurrentItem(mCurrentPosition, false);
}
});
break;
}
return super.dispatchTouchEvent(ev);
}
}
跟我思路看下吧
首先第一步應(yīng)該是viewpage三部曲
找到viewpage的id,載入data,綁定adpter;
這里需要說明的在載入data;
原來的處理方式:圖1斥铺,圖2彻桃,圖3
現(xiàn)在的處理方式:新圖3、圖1仅父、圖2、圖3浑吟、新圖1笙纤。
所以這里在更新的時(shí)候會(huì)清空mViews,重新進(jìn)行載入的時(shí)候是根據(jù)數(shù)據(jù)源和
新圖3、圖1组力、圖2省容、圖3、新圖1
這樣的方式去載入
mViews.clear();
//載入圖片的時(shí)候需要用的item,默認(rèn)用網(wǎng)絡(luò)圖片進(jìn)行加載
view = LayoutInflater.from(this).inflate(R.layout.item, null);
imageView = (ImageView) view.findViewById(R.id.image);
Glide.with(this)
.load(Imgs[Imgs.length - 1])
.centerCrop()
.into(imageView);
mViews.add(view);
for (int i = 0; i < Imgs.length; i++) {
view = LayoutInflater.from(this).inflate(R.layout.item, null);
imageView = (ImageView) view.findViewById(R.id.image);
Glide.with(this)
.load(Imgs[i])
.centerCrop()
.into(imageView);
mViews.add(view);
}
view = LayoutInflater.from(this).inflate(R.layout.item, null);
imageView = (ImageView) view.findViewById(R.id.image);
Glide.with(this)
.load(Imgs[0])
.centerCrop()
.into(imageView);
mViews.add(view);
myAdapter.notifyDataSetChanged();
這樣就實(shí)現(xiàn)轉(zhuǎn)換機(jī)制,接下來就是實(shí)現(xiàn)對(duì)viewpager的監(jiān)聽,實(shí)現(xiàn)三個(gè)方法
onPageSelected(int position):
這個(gè)方法有一個(gè)參數(shù)position燎字,代表哪個(gè)頁面被選中腥椒。* 當(dāng)用手指滑動(dòng)翻頁的時(shí)候阿宅,如果翻動(dòng)成功了(滑動(dòng)的距離夠長),手指抬起來就會(huì)立即執(zhí)行這個(gè)方法笼蛛,position就是當(dāng)前滑動(dòng)到的頁面洒放。* 如果直接setCurrentItem翻頁,那position就和setCurrentItem的參數(shù)一致滨砍,這種情況在onPageScrolled執(zhí)行方法前就會(huì)立即執(zhí)行往湿。*
而實(shí)現(xiàn) 新圖3、圖1惋戏、圖2领追、圖3、新圖1 這樣的效果就在這里實(shí)現(xiàn)
這里需要好好體會(huì)下
@Override
public void onPageSelected(int position) {
int max = mViews.size() - 1;
mCurrentPosition = position;
if (position == 0) {
mCurrentPosition = max - 1;
} else if (position == max) {
mCurrentPosition = 1;
}
onPageScrollStateChanged(int state)
這個(gè)方法在手指操作屏幕的時(shí)候發(fā)生變化响逢。有三個(gè)值:0(END),1(PRESS) , 2(UP) 绒窑。 當(dāng)用手指滑動(dòng)翻頁時(shí),手指按下去的時(shí)候會(huì)觸發(fā)這個(gè)方法舔亭,state值為1些膨,手指抬起時(shí),如果發(fā)生了滑動(dòng)(即使很蟹中)傀蓉,這個(gè)值會(huì)變?yōu)?,然后最后變?yōu)? 职抡。 總共執(zhí)行這個(gè)方法三次葬燎。一種特殊情況是手指按下去以后一點(diǎn)滑動(dòng)也沒有發(fā)生,這個(gè)時(shí)候只會(huì)調(diào)用這個(gè)方法兩次缚甩,state值分別是1,0 谱净。 當(dāng)setCurrentItem翻頁時(shí),會(huì)執(zhí)行這個(gè)方法兩次擅威,state值分別為2 , 0 壕探。
setCurrentItem(mCurrentPosition, false);方法其實(shí)就是跳轉(zhuǎn)到指定的頁碼,后面那個(gè)參數(shù)是動(dòng)畫效果
@Override
public void onPageScrollStateChanged(int state) {
if (state == 0) {
viewpager.setCurrentItem(mCurrentPosition, false);
}
}
});
到這里還不能實(shí)現(xiàn)圖片輪播,因?yàn)闆]有一個(gè)時(shí)間異步操作;
一般這里都會(huì)用Handler來操作,有了rxjava之后其實(shí)可以更加清晰的看出來
//調(diào)用rxjava來執(zhí)行
subscribe = Observable.interval(delay, TimeUnit.MILLISECONDS)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Long>() {
@Override
public void call(Long aLong) {
mCurrentPosition++;
if (viewpager != null)
viewpager.setCurrentItem(mCurrentPosition, false);
}
});
對(duì)于不熟悉rxjava和rxandroid的小伙伴可以去其他博客看看;
到這里就基本實(shí)現(xiàn)了圖片輪播效果,不添加效果圖了;
那么還有比較重要的一塊,指示器
對(duì)的,指示器也非常重要
因?yàn)槲业乃悸肥莢iew層和data層分開,在構(gòu)造方法中加入,這樣是為了方便加入點(diǎn)擊事件,詳情看代碼;
加入指示器或者小圓點(diǎn)實(shí)際上很簡單
其實(shí)ponit根布局也可以用代碼寫,只是覺得太麻煩了 ,所以寫在布局里面
size = Imgs.length;
mIndicators = new ImageView[size];
for (int i = 0; i < size; i++) {
mIndicators[i] = new ImageView(this);
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(10,10);
lp.setMargins(10, 0, 10, 0);
mIndicators[i].setLayoutParams(lp);
mIndicators[i].setBackgroundResource(R.drawable.selector_home_banner_points);
ponit.addView(mIndicators[i]);
}
這里添下布局文件的代碼,省得小伙伴們自己去寫
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@mipmap/bannertrue"
android:state_selected="true"/>
<item android:drawable="@mipmap/bannerfalse"/>
</selector>
我們后面回去調(diào)用這個(gè)布局
這里最關(guān)鍵的是 android:state_selected="true"
java調(diào)用到這個(gè)方法
mIndicators[i].setSelected(ponitpos==i);
關(guān)于小園點(diǎn)走向有兩種思路;
我先說下整體的思路 小圓點(diǎn)的走向應(yīng)給是viewpage滑動(dòng)的走向
那么怎么得到滑動(dòng)的走向
其實(shí)在之前代碼里面已經(jīng)寫了
ponitpos = mCurrentPosition - 1;
回過頭去看看這行,理解了就能知道
ponitpos = mCurrentPosition - 1;
for (int i = 0; i < mIndicators.length; i++) {
mIndicators[i].setSelected(ponitpos==i);
}
相對(duì)來說第二種思路是比較好理解,但其實(shí)就是第一種思路
viewpager.getCurrentItem();是獲取當(dāng)前頁碼的數(shù)量
新圖3(0)、圖1(1)郊丛、圖2(2)李请、圖3(3)、新圖1(4)
int c = viewpager.getCurrentItem();
Log.e("c",c+"");
for (int i = 0; i <mViews.size(); i++) {
if(c==0||c==mViews.size()-1){
break;
}else{
c = c-1;
break;
}
}
for (int i = 0; i < mIndicators.length; i++) {
mIndicators[i].setSelected(c==i);
}
基本到這里就實(shí)現(xiàn)了圖片的輪播和小圓點(diǎn)的加入
那么最后一些補(bǔ)充
手勢(shì)操作的判斷 不解釋了
// @Override
public boolean dispatchTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_DOWN:
// 手指按下或者滑動(dòng)的過程中停止輪播
// 手指按下或者滑動(dòng)的過程中停止輪播
subscribe.unsubscribe();
break;
case MotionEvent.ACTION_UP:
// 手指離開屏幕開始輪播
subscribe = Observable.interval(delay, TimeUnit.MILLISECONDS)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Long>() {
@Override
public void call(Long aLong) {
mCurrentPosition++;
if (viewpager != null)
viewpager.setCurrentItem(mCurrentPosition, false);
}
});
break;
}
return super.dispatchTouchEvent(ev);
}
最后就是默認(rèn)加載試圖是從1開始加載的;
這個(gè)很重要
還有一種實(shí)現(xiàn)方式,等下篇博客吧,用Handler來實(shí)現(xiàn)!
差點(diǎn)忘了代碼地址https://github.com/Chenshuai770/test_rx3.git
我是小帥,一起進(jìn)步啊!