作為一個剛工作的菜鳥释牺,連個焦點輪播圖都擼不出來没咙,真的是感覺沒臉見人,查閱多方資料涡驮,磕磕絆絆也算是做出來了捉捅,但由于不會寫寄月,大部分代碼也是拼湊起來的漾肮,代碼不美觀不說,復用起來也是極為的不易谭溉,尋思還是從頭整理一下,做個自定義View,日后復用旅挤,查看也更為順手粘茄。
說是自定義view,其實也只是組合view,將不同功能的view組合在一起磺平。
STEP.ONE 滑動展示圖片
通過ViewPager可以容易的實現(xiàn)可滑動的展示圖片的view擦酌。新建三個文件:
- custom_banner.xml闸英,自定義View布局
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v4.view.ViewPager
android:id="@+id/vp_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</RelativeLayout>
布局文件遇伞,很簡單巍耗,隨著功能的完善,還會添加view
- Banner.java,自定義view
public class Banner extends RelativeLayout {
private String TAG = "Banner";
private View mView;
private ViewPager mImage;
private List<Integer> mImageList = new ArrayList<>();
public Banner(Context context, AttributeSet attrs) {
super(context, attrs);
mView = LayoutInflater.from(getContext()).inflate(R.layout.custom_banner, this);
init();
}
private void init() {
mImage = mView.findViewById(R.id.vp_image);
mImageList = new ArrayList<>();
mImageList.add(R.mipmap.img_1);
mImageList.add(R.mipmap.img_2);
mImageList.add(R.mipmap.img_3);
ImageAdapter adapter = new ImageAdapter(mImage,mImageList);
mImage.setAdapter(adapter);
}
}
public Banner(Context context, AttributeSet attrs)引入布局帘靡,init()中簡單的添加了幾個數(shù)據(jù)涩赢,通過adapter加載赃阀。
- ImageAdapter.java,adapter
public class ImageAdapter extends PagerAdapter {
private String TAG = "ImageAdapter";
private ViewPager mImage;
private List<Integer> mImageList = new ArrayList<>();
private int DEFAULT_BANNER_SIZE;
private int FAKE_BANNER_SIZE;
public ImageAdapter( ViewPager mImage, List<Integer> mImageImageList) {
this.mImage = mImage;
this.mImageList = mImageImageList;
this.DEFAULT_BANNER_SIZE = mImageImageList.size();
this.FAKE_BANNER_SIZE = DEFAULT_BANNER_SIZE + DEFAULT_BANNER_SIZE + DEFAULT_BANNER_SIZE;
}
@Override
public int getCount() {
return FAKE_BANNER_SIZE;
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
position %= DEFAULT_BANNER_SIZE;
ImageView img = new ImageView(container.getContext());
img.setImageResource(mImageList.get(position));
container.addView(img);
return img;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
//super.destroyItem(container, position, object);
container.removeView((View) object);
}
}
instantiateItem()加載子view,定義了兩個變量DEFAULT_BANNER_SIZE和FAKE_BANNER_SIZE搪柑,在循環(huán)滑動時會用到聋丝。
到這里最簡單的ViewPager的用法了,這里還有一點需要注意工碾,ViewPager需要重寫onMeasure()方法弱睦,測量child的高度作為自身高度,否則會match_parent渊额,在banner.java中重寫onMeasure()况木。
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int maxHeight = 0;
for (int i = 0; i < mImage.getChildCount(); i++) {
View child = mImage.getChildAt(i);
measureChild(child, widthMeasureSpec, heightMeasureSpec);
maxHeight = Math.max(maxHeight,child.getMeasuredHeight());
}
heightMeasureSpec = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.EXACTLY);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
測量child的高度,最大值作為父容器高度旬迹。
STEP.TWO 實現(xiàn)循環(huán)與自動播放
實現(xiàn)循環(huán)重寫PagerAdapter中的finishUpdate(),自動播放使用輕量級的定時器Timer火惊。
- 循環(huán)。在ImageAdapter.java中重寫finishUpdate()奔垦。
public class ImageAdapter extends PagerAdapter {
private String TAG = "ImageAdapter";
private ViewPager mImage;
private List<Integer> mImageList = new ArrayList<>();
private int DEFAULT_BANNER_SIZE;
private int FAKE_BANNER_SIZE;
public ImageAdapter( ViewPager mImage, List<Integer> mImageImageList) {
this.mImage = mImage;
this.mImageList = mImageImageList;
this.DEFAULT_BANNER_SIZE = mImageImageList.size();
this.FAKE_BANNER_SIZE = DEFAULT_BANNER_SIZE + DEFAULT_BANNER_SIZE + DEFAULT_BANNER_SIZE;
}
@Override
public int getCount() {
return FAKE_BANNER_SIZE;
.
.
.
@Override
public void finishUpdate(ViewGroup container) {
super.finishUpdate(container);
int curPosition = mImage.getCurrentItem();
if (0 == curPosition){
//滑動到最左邊
mImage.setCurrentItem(DEFAULT_BANNER_SIZE, false);
}
if ((FAKE_BANNER_SIZE -1) == curPosition){
//滑動到最右邊
mImage.setCurrentItem(FAKE_BANNER_SIZE - DEFAULT_BANNER_SIZE - 1, false);
}
}
}
DEFAULT_BANNER_SIZE是圖片列表的大小屹耐,F(xiàn)AKE_BANNER_SIZE是三倍圖片列表的小。
以代碼為例椿猎,目前我們的ViewPager有3屏(DEFAULT_BANNER_SIZE)惶岭,但是如果在PagerAdapter的getCount方法中我們返回9(FAKE_BANNER_SIZE),即告訴ViewPager我們有9屏鸵贬。那么如果當前位于第3屏俗他,由于對于ViewPager來說它認為有9屏,所以我們?nèi)匀豢梢韵蚝蠡瑒永啤.敾瑒拥降?屏時(右邊界)兆衅,立即將第6屏的view返回給他,這樣ViewPager依然可以向右滑動嗜浮。同理處于第1屏時(左邊界)羡亩,講第4屏的view返回給他,此時仍然可以左滑危融。這樣便實現(xiàn)了循環(huán)播放畏铆。如圖..............hahah
- 自動播放
public class Banner extends RelativeLayout {
private int DEFAULT_BANNER_SIZE;
private int FAKE_BANNER_SIZE;
//{start update 實現(xiàn)自動播放
@SuppressLint("HandlerLeak")
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
case 1:
int position = (mImage.getCurrentItem() + 1) % DEFAULT_BANNER_SIZE + DEFAULT_BANNER_SIZE;
mImage.setCurrentItem(position, true);
Log.d(TAG, "handleMessage: "+position);
break;
default:
break;
}
}
};
// end }
private List<Integer> mImageList = new ArrayList<>();
public Banner(Context context, AttributeSet attrs) {
super(context, attrs);
mView = LayoutInflater.from(getContext()).inflate(R.layout.custom_banner, this);
init();
//調(diào)用
startTimer();
}
//{start update
private void startTimer() {
Timer timer = new Timer();
TimerTask task = new TimerTask() {
@Override
public void run() {
Message message = new Message();
message.what = 1;
handler.sendMessage(message);
}
};
timer.schedule(task,5000,5000);
}
//end }
}
使用Timer定義一個5秒執(zhí)行的任務,通過Headler異步執(zhí)行吉殃。
STEP.THREE 底部圓點
根據(jù)mImageList動態(tài)添加底部圓點辞居。
- 修改custom_banner.xml文件
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v4.view.ViewPager
android:id="@+id/vp_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<LinearLayout
android:id="@+id/ll_point"
android:layout_centerHorizontal="true"
android:layout_alignBottom="@+id/vp_image"
android:layout_marginBottom="10dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal" />
</RelativeLayout>
新增了LinearLayout布局楷怒,添加圓點的任務就交給他了。
- 修改Banner.java
private void initPoint() {
for (int i = 0; i < mImageList.size(); i++) {
View view = new View(getContext());
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(15, 15);
params.leftMargin = 10;
params.rightMargin = 10;
view.setLayoutParams(params);
if (i == 0) {
view.setBackgroundResource(R.drawable.shape_point_select);
} else {
view.setBackgroundResource(R.drawable.shape_point_default);
}
final int finalI = i;
view.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
int item = finalI+DEFAULT_BANNER_SIZE;
mImage.setCurrentItem(item, true);
}
});
mPoint.addView(view);
mPointList.add(view);
}
}
在mImageList初始化后執(zhí)行該函數(shù)瓦灶,添加圓點鸠删,背景,和點擊事件贼陶。
- 發(fā)生點擊事件后刃泡,修改圓點背景
mImage.addOnPageChangeListener(this);
@Override
public void onPageSelected(int position) {
position %= DEFAULT_BANNER_SIZE;
for (View view : mPointList) {
view.setBackgroundResource(R.drawable.shape_point_default);
}
mPointList.get(position).setBackgroundResource(R.drawable.shape_point_select);
}
添加監(jiān)聽,當某頁面被選中時碉怔,先遍歷列表烘贴,將所有圓點都設置為未選中狀態(tài),再將對應頁面的背景設置為選中背景撮胧。
END
到這里這個功能就已經(jīng)全部實現(xiàn)了桨踪,這也是我的第一篇技術文章,參考了挺多大大的博客趴樱,最后倒騰出來馒闷。文章感覺寫的干巴巴酪捡,一來我本人對有些也不算特別熟悉叁征,二來文章寫的少。堅持寫博客逛薇,到自己的文章干貨越來越多捺疼,越寫越寫流暢,自己的成長了多少也就很明確了永罚。當然也希望這篇文章可以幫助到大家啤呼。
參考鏈接
循環(huán)廣告位組件的實現(xiàn)
動態(tài)圓點輪播圖
解決ViewPager高度無法wrap_content問題