Android 自定義一個輪播圖
有限空間內展示更多的內容,輪播圖是個不錯的選擇,本文將實現一個輪播圖SlideShowView,效果如下圖所示:
因為輪播圖的每一個頁面都有文字和圖片,為了整合圖片和文字,SlideShowView選擇繼承Fraement
public class SlideShowView extends FrameLayout輪播圖的主要屬性如下:
/*儲存圖片連結*/ private String[] imageUrls; /*儲存文字內容*/ private String[] contents; /*ViewPager裝圖片和文字*/ private ViewPager viewPager; /*右下邊起到指示作用的小圓點*/ private List<View> dotViewsList; /*定時功能,自動輪播*/ private ScheduledExecutorService scheduledExecutorService; /*記錄輪播下標*/ private int currentItem = 0;
輪播圖要求定時更換展示的內容,定時功能使用ScheduledExecutorService實現
/*開啟輪播*/ public void startPlay(){ scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(); scheduledExecutorService.scheduleAtFixedRate(new SlideShowTask(), 1, 5, TimeUnit.SECONDS); }定時時間到的話將會執行SlideShowTask任務,該任務主要是切換頁面,這裡更換下標即可,子執行緒不能更新UI,所以使用handler將訊息發給主執行緒
private class SlideShowTask implements Runnable { @Override public void run() { synchronized (viewPager) { currentItem = (currentItem+1)%imageUrls.length; handler.obtainMessage().sendToTarget(); } } }handler在主執行緒中接受換頁訊息,執行換頁操作。為了避免handler記憶體洩露,將內部handler定義為靜態的,保持一個外部類的弱引用
private Handler handler = new SafeHandler(this); static class SafeHandler extends Handler { WeakReference<SlideShowView> mOuter; SafeHandler(SlideShowView v) { mOuter= new WeakReference<SlideShowView>(v); } @Override public void handleMessage(Message msg) { final SlideShowView v = mOuter.get(); super.handleMessage(msg); if (v != null) { v.viewPager.setCurrentItem(v.currentItem); } } }由於開啟了執行緒池和handler,為了防止生命週期問題導致的記憶體洩露,在輪播圖離開檢視被銷燬的時候要關閉執行緒池並且清除handler訊息回撥
/*關閉輪播*/ public void stopPlay() { if(scheduledExecutorService!=null){ scheduledExecutorService.shutdown(); scheduledExecutorService=null; } }
@Override public void onDetachedFromWindow() { super.onDetachedFromWindow(); stopPlay(); handler.removeCallbacksAndMessages(null); }為了載入圖片,使用Picasso框架,根據url載入圖片並顯示在控制元件上
Picasso.with(context) .load(imageUrls[position]) .placeholder(R.mipmap.slide_show_view_loading) .error(R.mipmap.slide_show_view_loading) .config(Bitmap.Config.RGB_565) /*.memoryPolicy(MemoryPolicy.NO_CACHE, MemoryPolicy.NO_STORE)*/ .into(image);右下角小紅點指示器是一個LinearLayout,根據頁面數量動態生成
/*小紅點指示標誌處理*/ for (int i = 0; i < imageUrls.length; i++) { ImageView dotView = new ImageView(context); dotView.setId(i); LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); params.leftMargin = 4; params.rightMargin = 4; dotLayout.addView(dotView, params); dotViewsList.add(dotView); } ImageView[] dotView = new ImageView[imageUrls.length]; for(int i = 0;i<imageUrls.length;i++){ dotView[i]= (ImageView) findViewById(i); dotView[i].setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { viewPager.setCurrentItem(v.getId()); } }); }ViewPager監聽介面滑動,自己實現一個 ViewPager.OnPageChangeListener即可
完整程式碼如下:
public class SlideShowView extends FrameLayout{
private static final String TAG = "SlideShowView";
private Context context;
/*儲存圖片連結*/
private String[] imageUrls;
/*儲存文字內容*/
private String[] contents;
/*ViewPager裝圖片和文字*/
private ViewPager viewPager;
/*右下邊起到指示作用的小圓點*/
private List<View> dotViewsList;
/*定時功能,自動輪播*/
private ScheduledExecutorService scheduledExecutorService;
/*記錄輪播下標*/
private int currentItem = 0;
public SlideShowView(Context context) {
this(context, null);
}
public SlideShowView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SlideShowView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.context = context;
}
/*傳入圖片連結和文字內容*/
public void initData(String[] imageUrls,String[] contents) {
this.imageUrls = imageUrls;
this.contents = contents;
dotViewsList = new ArrayList<>();
initUI(context);
}
/*開啟輪播*/
public void startPlay(){
scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
scheduledExecutorService.scheduleAtFixedRate(new SlideShowTask(), 1, 5, TimeUnit.SECONDS);
}
/**
* 發現使用這個輪播圖的地方沒有關閉執行緒池,導致記憶體嚴重洩露
* by:wangshihui
* at:2016/4/25 0025 10:29
*/
/*關閉輪播*/
public void stopPlay() {
if(scheduledExecutorService!=null){
scheduledExecutorService.shutdown();
scheduledExecutorService=null;
}
}
/*檢視銷燬釋放資源
* This is called when the view is detached from a window.*/
@Override
public void onDetachedFromWindow() {
super.onDetachedFromWindow();
stopPlay();
handler.removeCallbacksAndMessages(null);
}
/**
* Handler的正確用法
* by:wangshihui
* at:2016/4/28 0028 10:47
*/
/*使用Handler注意記憶體洩露的問題*/
private Handler handler = new SafeHandler(this);
static class SafeHandler extends Handler {
WeakReference<SlideShowView> mOuter;
SafeHandler(SlideShowView v) {
mOuter= new WeakReference<SlideShowView>(v);
}
@Override
public void handleMessage(Message msg) {
final SlideShowView v = mOuter.get();
super.handleMessage(msg);
if (v != null) {
v.viewPager.setCurrentItem(v.currentItem);
}
}
}
/**
*執行輪播圖切換任務
*/
private class SlideShowTask implements Runnable {
@Override
public void run() {
synchronized (viewPager) {
currentItem = (currentItem+1)%imageUrls.length;
handler.obtainMessage().sendToTarget();
}
}
}
/**
* ViewPager的監聽器
* 當ViewPager中頁面的狀態發生改變時呼叫
*/
private class ViewPagerPageChangeListener implements ViewPager.OnPageChangeListener {
boolean isPlaying = false;
@Override
public void onPageScrollStateChanged(int state) {
switch (state) {
/*正在滑動或拖動*/
case 1:
isPlaying = false;
break;
/*滑動結束*/
case 2:
isPlaying = true;
break;
/*什麼都沒做*/
case 0:
/*滑動到頭尾後迴圈滑動處理*/
if (viewPager.getCurrentItem() == viewPager.getAdapter().getCount() - 1 && !isPlaying) {
viewPager.setCurrentItem(0);
}
else if (viewPager.getCurrentItem() == 0 && !isPlaying) {
viewPager.setCurrentItem(viewPager.getAdapter().getCount() - 1);
}
break;
}
}
@Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
}
@Override
public void onPageSelected(int pos) {
currentItem = pos;
/*設定下邊的小圓點指示標誌*/
for(int i=0;i < dotViewsList.size();i++){
if(i == pos){
dotViewsList.get(pos).setBackgroundResource(R.mipmap.dot_focus);
}else {
dotViewsList.get(i).setBackgroundResource(R.mipmap.dot_blur);
}
}
}
}
private void initUI(final Context context) {
View view = LayoutInflater.from(context).inflate(R.layout.viewpager_with_net, this,true);
LinearLayout dotLayout = (LinearLayout)findViewById(R.id.dotLayout);
/*手動播放時上一頁下一頁按鈕*/
ImageView front = (ImageView) view.findViewById(R.id.back);
ImageView next = (ImageView) view.findViewById(R.id.more);
front.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if(currentItem>0){
currentItem = (currentItem-1)%imageUrls.length;
handler.obtainMessage().sendToTarget();
}else{
currentItem =imageUrls.length-1;
handler.obtainMessage().sendToTarget();
}
}
});
next.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if(currentItem<imageUrls.length){
currentItem = (currentItem+1)%imageUrls.length;
handler.obtainMessage().sendToTarget();
}
}
});
/*小紅點指示標誌處理*/
for (int i = 0; i < imageUrls.length; i++) {
ImageView dotView = new ImageView(context);
dotView.setId(i);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
params.leftMargin = 4;
params.rightMargin = 4;
dotLayout.addView(dotView, params);
dotViewsList.add(dotView);
}
ImageView[] dotView = new ImageView[imageUrls.length];
for(int i = 0;i<imageUrls.length;i++){
dotView[i]= (ImageView) findViewById(i);
dotView[i].setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
viewPager.setCurrentItem(v.getId());
}
});
}
viewPager = (ViewPager) view.findViewById(R.id.view);
viewPager.setAdapter(new ViewPagerAdapter());
viewPager.setOnPageChangeListener(new ViewPagerPageChangeListener());
}
private class ViewPagerAdapter extends PagerAdapter {
@Override
public int getCount() {
return imageUrls.length;
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view ==object;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
View view = LayoutInflater.from(context).inflate(R.layout.viewpager_item, null);
TextView content = (TextView) view.findViewById(R.id.content);
ImageView image = (ImageView) view.findViewById(R.id.imageview);
content.setText(contents[position]);
Logger.d(imageUrls[position]);
/*檢視大圖放棄memory cache
在檢視大圖時放棄使用記憶體快取,圖片從網路下載完成後會快取到磁碟中
載入會從磁碟中載入,這樣可以加速記憶體的回收。
其中memoryPolicy的NO_CACHE是指圖片載入時放棄在記憶體快取中查詢,
NO_STORE是指圖片載入完不快取在記憶體中。*/
Picasso.with(context)
.load(imageUrls[position])
.placeholder(R.mipmap.slide_show_view_loading)
.error(R.mipmap.slide_show_view_loading)
.config(Bitmap.Config.RGB_565)
/*.memoryPolicy(MemoryPolicy.NO_CACHE, MemoryPolicy.NO_STORE)*/
.into(image);
container.addView(view);
return view;
}
@Override
public void destroyItem(View container, int position, Object object) {
((ViewPager) container).removeView((View) object);
}
}
}
佈局檔案:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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/view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ImageView
android:id="@+id/back"
android:layout_gravity="left"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_centerInParent="true"
android:layout_alignParentLeft="true"
android:paddingLeft="5dp"
android:paddingRight="5dp"
android:src="@mipmap/backa"/>
<ImageView
android:layout_gravity="right"
android:id="@+id/more"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_centerInParent="true"
android:layout_alignParentRight="true"
android:paddingRight="5dp"
android:paddingLeft="5dp"
android:src="@mipmap/nexta"/>
<LinearLayout android:id="@+id/dotLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dp"
android:layout_gravity="bottom|right"
android:gravity="right"
android:orientation="horizontal">
</LinearLayout>
</FrameLayout>
相關文章
- Android 自定義輪播圖片控制元件Android控制元件
- uniapp自定義卡片輪播圖APP
- GKCycleScrollView - 一個輕量級的自定義輪播圖元件View元件
- 自定義圖片輪播控制元件控制元件
- 小程式輪播圖自定義指示器
- 自定義view————Banner輪播View
- 【iOS】自定義控制元件無限輪播 + 無限圖片輪播iOS控制元件
- 【Swift】自定義控制元件無限輪播 + 無限圖片輪播Swift控制元件
- Android自定義View實現文字輪播效果AndroidView
- carousel 輪播自定義進度條
- 用RecyclerView打造一個輪播圖View
- 手機直播原始碼,android 輪播圖自定製元件原始碼Android元件
- Flutter 封裝一個 Banner 輪播圖Flutter封裝
- Android 和 iOS 圖片輪播AndroidiOS
- Flutter 如何封裝一個 Banner 輪播圖?Flutter封裝
- 一分鐘學會如何自定義小程式輪播圖(蜜雪冰城Demo)
- php短視訊原始碼,jQuery實現自定義輪播圖外掛PHP原始碼jQuery
- jQuery輪播圖之上下輪播jQuery
- android: 傳送自定義廣播Android
- Android自定義ImageView 在圖片上新增一個圖層AndroidView
- 微信小程式------輪播圖------縱向輪播圖微信小程式
- VUE開發一個圖片輪播的元件Vue元件
- 用RecyclerView打造一個輪播圖(進階版)View
- Android輪播圖從0到1Android
- Flutter輪播圖Flutter
- vue輪播圖Vue
- iOS 輪播圖iOS
- 記一個JavaScript圖片輪播思路與程式碼JavaScript
- 原生 JS 擼一個輪播圖(支援拖拽切屏)JS
- 教你如何用 RecyclerView 做一個好用的輪播圖View
- 大三學生的第一個輪播圖元件元件
- 面向Vue新人:使用Vue寫一個圖片輪播元件Vue元件
- js 輪播圖 (原生)JS
- jQuery Mobile圖片輪轉輪播jQuery
- 文字輪播與圖片輪播?CSS 不在話下CSS
- 一對一直播系統原始碼,軟體首頁輪播圖輪播效果原始碼
- 高效圖片輪播,兩個ImageView實現View
- 一個基於Vue的圖片輪播元件的實現Vue元件