Android自定義控制元件BannerLayout,實現廣告輪播
阿新 • • 發佈:2019-02-12
Android自定義廣告輪播圖
自定義的BannerLayout,通過ViewPager來實現,配合Glide 實現本地以及網路圖片的載入。
效果:
專案結構:
- 在build.gradle中新增對Glide (圖片載入框架)的引用:
2.AndroidManifest.xml中開網路許可權compile 'com.github.bumptech.glide:glide:3.7.0'
<uses-permissionandroid:name="android.permission.INTERNET"/>
3.BannerLayout檔案
4.attr.xml中package com.example.zhh.mytestbanner; import android.content.Context; import android.content.res.TypedArray; import android.graphics.drawable.Drawable; import android.graphics.drawable.GradientDrawable; import android.graphics.drawable.LayerDrawable; import android.os.Handler; import android.os.Message; import android.support.annotation.NonNull; import android.support.v4.view.PagerAdapter; import android.support.v4.view.ViewPager; import android.text.TextUtils; import android.util.AttributeSet; import android.util.Log; import android.view.Gravity; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.animation.Interpolator; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.RelativeLayout; import android.widget.Scroller; import android.widget.TextView; import com.bumptech.glide.Glide; import com.squareup.picasso.Picasso; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; /** * 輪播圖元件 * Created by TwoSX on 2016/3/30. */ public class BannerLayout extends RelativeLayout { private ViewPager mViewPager; // 輪播容器 // 指示器(圓點)容器 private LinearLayout indicatorContainer; private Drawable unSelectedDrawable; private Drawable selectedDrawable; private int WHAT_AUTO_PLAY = 1000; private boolean isAutoPlay = true; // 自動輪播 private int itemCount; private int selectedIndicatorColor = 0xffff0000; private int unSelectedIndicatorColor = 0x88888888; private int titleBGColor = 0X55000000; private int titleColor = 0Xffffffff; private Shape indicatorShape = Shape.oval; private int selectedIndicatorHeight = 20; private int selectedIndecatorWidth = 20; private int unSelectedIndicatorHeight = 20; private int unSelectedIndecatorWidth = 20; private int autoPlayDuration = 4000; private int scrollDuration = 900; // 指示器中點和點之間的距離 private int indicatorSpace = 15; // 指示器整體的設定 private int indicatorMargin = 20; private static final int titlePadding = 20; private int defaultImage; private enum Shape { // 矩形 或 圓形的指示器 rect, oval } private OnBannerItemClickListener mOnBannerItemClickListener; private Handler mHandler = new Handler(new Handler.Callback() { @Override public boolean handleMessage(Message msg) { if (msg.what == WHAT_AUTO_PLAY) { if (mViewPager != null) { mViewPager.setCurrentItem(mViewPager.getCurrentItem() + 1, true); mHandler.sendEmptyMessageDelayed(WHAT_AUTO_PLAY, autoPlayDuration); } } return false; } }); public BannerLayout(Context context) { super(context); init(null, 0); } public BannerLayout(Context context, AttributeSet attrs) { super(context, attrs); init(attrs, 0); } public BannerLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(attrs, defStyleAttr); } private void init(AttributeSet attrs, int defStyleAttr) { TypedArray array = getContext().obtainStyledAttributes(attrs, R.styleable.BannerLayoutStyle, defStyleAttr, 0); selectedIndicatorColor = array.getColor(R.styleable.BannerLayoutStyle_selectedIndicatorColor, selectedIndicatorColor); unSelectedIndicatorColor = array.getColor(R.styleable.BannerLayoutStyle_unSelectedIndicatorColor, unSelectedIndicatorColor); titleBGColor = array.getColor(R.styleable.BannerLayoutStyle_titleBGColor, titleBGColor); titleColor = array.getColor(R.styleable.BannerLayoutStyle_titleColor, titleColor); int shape = array.getInt(R.styleable.BannerLayoutStyle_indicatorShape, Shape.oval.ordinal()); for (Shape shape1 : Shape.values()) { if (shape1.ordinal() == shape) { indicatorShape = shape1; break; } } selectedIndecatorWidth = (int) array.getDimension(R.styleable.BannerLayoutStyle_selectedIndicatorWidth, selectedIndecatorWidth); selectedIndicatorHeight = (int) array.getDimension(R.styleable.BannerLayoutStyle_selectedIndicatorHeight, selectedIndicatorHeight); unSelectedIndecatorWidth = (int) array.getDimension(R.styleable.BannerLayoutStyle_unSelectedIndicatorWidth, unSelectedIndecatorWidth); unSelectedIndicatorHeight = (int) array.getDimension(R.styleable.BannerLayoutStyle_unSelectedIndicatorHeight, unSelectedIndicatorHeight); indicatorSpace = (int) array.getDimension(R.styleable.BannerLayoutStyle_indicatorSpace, indicatorSpace); indicatorMargin = (int) array.getDimension(R.styleable.BannerLayoutStyle_indicatorMargin, indicatorMargin); autoPlayDuration = array.getInt(R.styleable.BannerLayoutStyle_autoPlayDuration, autoPlayDuration); scrollDuration = array.getInt(R.styleable.BannerLayoutStyle_scrollDuration, scrollDuration); isAutoPlay = array.getBoolean(R.styleable.BannerLayoutStyle_isAutoPlay, isAutoPlay); defaultImage = array.getResourceId(R.styleable.BannerLayoutStyle_defaultImage, defaultImage); array.recycle(); LayerDrawable unSelectedLayerDrawable; LayerDrawable selectedLayerDrawable; GradientDrawable unSelectedGradientDrawable = new GradientDrawable(); GradientDrawable selectedGradientDtawbale = new GradientDrawable(); switch (indicatorShape) { case rect: unSelectedGradientDrawable.setShape(GradientDrawable.RECTANGLE); selectedGradientDtawbale.setShape(GradientDrawable.RECTANGLE); break; case oval: unSelectedGradientDrawable.setShape(GradientDrawable.OVAL); selectedGradientDtawbale.setShape(GradientDrawable.OVAL); break; } unSelectedGradientDrawable.setColor(unSelectedIndicatorColor); unSelectedGradientDrawable.setSize(unSelectedIndecatorWidth, unSelectedIndicatorHeight); unSelectedLayerDrawable = new LayerDrawable(new Drawable[]{unSelectedGradientDrawable}); unSelectedDrawable = unSelectedLayerDrawable; selectedGradientDtawbale.setColor(selectedIndicatorColor); selectedGradientDtawbale.setSize(selectedIndecatorWidth, selectedIndicatorHeight); selectedLayerDrawable = new LayerDrawable(new Drawable[]{selectedGradientDtawbale}); selectedDrawable = selectedLayerDrawable; } /** * 新增本地圖片 * * @param viewRes 圖片id集合 * @param titles 標題集合,可空 */ public void setViewRes(List<Integer> viewRes, List<String> titles) { List<View> views = new ArrayList<>(); itemCount = viewRes.size(); if (titles != null && titles.size() != viewRes.size()) { throw new IllegalStateException("views.size() != titles.size()"); } // 把數量拼湊到三個以上 if (itemCount < 1) { throw new IllegalStateException("item count not equal zero"); } else if (itemCount < 2) { views.add(getFrameLayoutView(viewRes.get(0), titles != null ? titles.get(0) : null, 0)); views.add(getFrameLayoutView(viewRes.get(0), titles != null ? titles.get(0) : null, 0)); views.add(getFrameLayoutView(viewRes.get(0), titles != null ? titles.get(0) : null, 0)); } else if (itemCount < 3) { views.add(getFrameLayoutView(viewRes.get(0), titles != null ? titles.get(0) : null, 0)); views.add(getFrameLayoutView(viewRes.get(1), titles != null ? titles.get(1) : null, 1)); views.add(getFrameLayoutView(viewRes.get(0), titles != null ? titles.get(0) : null, 0)); views.add(getFrameLayoutView(viewRes.get(1), titles != null ? titles.get(1) : null, 1)); } else { for (int i = 0; i < viewRes.size(); i++) { views.add(getFrameLayoutView(viewRes.get(i), titles != null ? titles.get(i) : null, i)); } } setViews(views); } //新增網路圖片路徑 public void setViewUrls(Context context,List<String> urls, List<String> titles) { List<View> views = new ArrayList<>(); itemCount = urls.size(); if (titles != null && titles.size() != itemCount) { throw new IllegalStateException("views.size() != titles.size()"); } //主要是解決當item為小於3個的時候滑動有問題,這裡將其拼湊成3個以上 if (itemCount < 1) {//當item個數0 throw new IllegalStateException("item count not equal zero"); } else if (itemCount < 2) { //當item個數為1 views.add(getFrameLayoutView(context,urls.get(0), titles != null ? titles.get(0) : null, 0)); views.add(getFrameLayoutView(context,urls.get(0), titles != null ? titles.get(0) : null, 0)); views.add(getFrameLayoutView(context,urls.get(0), titles != null ? titles.get(0) : null, 0)); } else if (itemCount < 3) {//當item個數為2 views.add(getFrameLayoutView(context,urls.get(0), titles != null ? titles.get(0) : null, 0)); views.add(getFrameLayoutView(context,urls.get(1), titles != null ? titles.get(1) : null, 1)); views.add(getFrameLayoutView(context,urls.get(0), titles != null ? titles.get(0) : null, 0)); views.add(getFrameLayoutView(context,urls.get(1), titles != null ? titles.get(1) : null, 1)); } else { for (int i = 0; i < urls.size(); i++) { views.add(getFrameLayoutView(context,urls.get(i), titles != null ? titles.get(i) : null, i)); } } setViews(views); } private void setViews(final List<View> views) { mViewPager = new ViewPager(getContext()); addView(mViewPager); setSliderTransformDuration(scrollDuration); //初始化indicatorContainer indicatorContainer = new LinearLayout(getContext()); indicatorContainer.setGravity(Gravity.CENTER_VERTICAL); LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); // 設定 指示點的位置 params.addRule(RelativeLayout.CENTER_HORIZONTAL); params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); //設定margin params.setMargins(indicatorMargin, indicatorMargin, indicatorMargin, indicatorMargin); //新增指示器容器佈局到SliderLayout addView(indicatorContainer, params); //初始化指示器,並新增到指示器容器佈局 for (int i = 0; i < itemCount; i++) { ImageView indicator = new ImageView(getContext()); indicator.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)); indicator.setPadding(indicatorSpace, indicatorSpace, indicatorSpace, indicatorSpace); indicator.setImageDrawable(unSelectedDrawable); indicatorContainer.addView(indicator); } LoopPagerAdapter pagerAdapter = new LoopPagerAdapter(views); mViewPager.setAdapter(pagerAdapter); //設定當前item到Integer.MAX_VALUE中間的一個值,看起來像無論是往前滑還是往後滑都是ok的 //如果不設定,使用者往左邊滑動的時候已經劃不動了 int targetItemPosition = Integer.MAX_VALUE / 2 - Integer.MAX_VALUE / 2 % itemCount; mViewPager.setCurrentItem(targetItemPosition); switchIndicator(targetItemPosition % itemCount); mViewPager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() { @Override public void onPageSelected(int position) { switchIndicator(position % itemCount); } }); startAutoPlay(); } /** * 開始自動輪播 */ public void startAutoPlay() { stopAutoPlay(); // 避免重複訊息 if (isAutoPlay) { mHandler.sendEmptyMessageDelayed(WHAT_AUTO_PLAY, autoPlayDuration); } } @Override protected void onWindowVisibilityChanged(int visibility) { super.onWindowVisibilityChanged(visibility); if (visibility == VISIBLE) { startAutoPlay(); } else { stopAutoPlay(); } } /** * 停止自動輪播 */ public void stopAutoPlay() { if (isAutoPlay) { mHandler.removeMessages(WHAT_AUTO_PLAY); } } private void setSliderTransformDuration(int scrollDuration) { try { Field mScroller = ViewPager.class.getDeclaredField("mScroller"); mScroller.setAccessible(true); FixedSpeedScroller fixedSpeedScroller = new FixedSpeedScroller(mViewPager.getContext(), null, scrollDuration); mScroller.set(mViewPager, fixedSpeedScroller); } catch (Exception e) { e.printStackTrace(); } } @Override public boolean dispatchTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: stopAutoPlay(); break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: startAutoPlay(); break; } return super.dispatchTouchEvent(ev); } /** * 切換指示器狀態 * * @param currentPosition 當前位置 */ private void switchIndicator(int currentPosition) { for (int i = 0; i < indicatorContainer.getChildCount(); i++) { ((ImageView) indicatorContainer.getChildAt(i)).setImageDrawable(i == currentPosition ? selectedDrawable : unSelectedDrawable); } } public void setOnBannerItemClickListener(OnBannerItemClickListener onBannerItemClickListener) { this.mOnBannerItemClickListener = onBannerItemClickListener; } @NonNull private FrameLayout getFrameLayoutView(Context context, String url, String title, final int position) { FrameLayout frameLayout = new FrameLayout(getContext()); FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); layoutParams.gravity = Gravity.CENTER; ImageView imageView = new ImageView(getContext()); frameLayout.addView(imageView); frameLayout.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (mOnBannerItemClickListener != null) { mOnBannerItemClickListener.onItemClick(position); } } }); imageView.setScaleType(ImageView.ScaleType.CENTER_CROP); if (defaultImage != 0){ Glide.with(getContext()).load(url).placeholder(defaultImage).centerCrop().into(imageView); }else { Glide.with(getContext()).load(url).centerCrop().into(imageView); } if (!TextUtils.isEmpty(title)) { TextView textView = new TextView(getContext()); textView.setText(title); textView.setTextColor(titleColor); textView.setPadding(titlePadding, titlePadding, titlePadding, titlePadding); textView.setBackgroundColor(titleBGColor); textView.getPaint().setFakeBoldText(true); layoutParams.gravity = Gravity.BOTTOM; frameLayout.addView(textView, layoutParams); } return frameLayout; } @NonNull private FrameLayout getFrameLayoutView(Integer res, String title, final int position) { FrameLayout frameLayout = new FrameLayout(getContext()); FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); layoutParams.gravity = Gravity.CENTER; ImageView imageView = new ImageView(getContext()); frameLayout.addView(imageView); frameLayout.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (mOnBannerItemClickListener != null) { mOnBannerItemClickListener.onItemClick(position); } } }); imageView.setScaleType(ImageView.ScaleType.CENTER_CROP); Glide.with(getContext()).load(res).centerCrop().into(imageView); if (!TextUtils.isEmpty(title)) { TextView textView = new TextView(getContext()); textView.setText(title); textView.setTextColor(titleColor); textView.setPadding(titlePadding, titlePadding, titlePadding, titlePadding); textView.setBackgroundColor(titleBGColor); textView.getPaint().setFakeBoldText(true); layoutParams.gravity = Gravity.BOTTOM; frameLayout.addView(textView, layoutParams); } return frameLayout; } public interface OnBannerItemClickListener { void onItemClick(int position); } public class LoopPagerAdapter extends PagerAdapter { private List<View> views; public LoopPagerAdapter(List<View> views) { this.views = views; } @Override public int getCount() { //Integer.MAX_VALUE = 2147483647 return Integer.MAX_VALUE; } @Override public boolean isViewFromObject(View view, Object object) { return view == object; } @Override public Object instantiateItem(ViewGroup container, int position) { if (views.size() > 0) { //position % view.size()是指虛擬的position會在[0,view.size())之間迴圈 View view = views.get(position % views.size()); if (container.equals(view.getParent())) { container.removeView(view); } container.addView(view); return view; } return null; } @Override public void destroyItem(ViewGroup container, int position, Object object) { } } public class FixedSpeedScroller extends Scroller { private int mDuration = 1000; public FixedSpeedScroller(Context context) { super(context); } public FixedSpeedScroller(Context context, Interpolator interpolator) { super(context, interpolator); } public FixedSpeedScroller(Context context, Interpolator interpolator, int duration) { this(context, interpolator); mDuration = duration; } @Override public void startScroll(int startX, int startY, int dx, int dy, int duration) { // Ignore received duration, use fixed one instead super.startScroll(startX, startY, dx, dy, mDuration); } @Override public void startScroll(int startX, int startY, int dx, int dy) { // Ignore received duration, use fixed one instead super.startScroll(startX, startY, dx, dy, mDuration); } } }
<?xml version="1.0" encoding="utf-8"?> <resources> <attr name="visible_tab_count" format="integer"/> <declare-styleable name="BannerLayoutStyle"> <attr name="selectedIndicatorColor" format="color|reference"/> <attr name="unSelectedIndicatorColor" format="color|reference"/> <attr name="titleBGColor" format="color|reference"/> <attr name="titleColor" format="color|reference"/> <attr name="indicatorShape" format="enum"> <enum name="rect" value="0"/> <enum name="oval" value="1"/> </attr> <attr name="selectedIndicatorHeight" format="dimension|reference"/> <attr name="selectedIndicatorWidth" format="dimension|reference"/> <attr name="unSelectedIndicatorHeight" format="dimension|reference"/> <attr name="unSelectedIndicatorWidth" format="dimension|reference"/> <attr name="indicatorSpace" format="dimension|reference"/> <attr name="indicatorMargin" format="dimension|reference"/> <attr name="autoPlayDuration" format="integer|reference"/> <attr name="scrollDuration" format="integer|reference"/> <attr name="isAutoPlay" format="boolean"/> <attr name="defaultImage" format="integer|reference"/> </declare-styleable> </resources>
5.activity_main.xml
6.MainActivity<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.twosx.app.bannerdemo.MainActivity"> <com.example.zhh.mytestbanner.BannerLayout android:id="@+id/banner1" android:layout_width="match_parent" android:layout_height="180dp" app:defaultImage="@mipmap/ic_launcher" app:indicatorMargin="10dp" app:indicatorShape="oval" app:indicatorSpace="2dp" app:scrollDuration="1100" app:selectedIndicatorColor="?attr/colorPrimary" app:selectedIndicatorHeight="6dp" app:selectedIndicatorWidth="6dp" app:titleColor="#ff0000" app:unSelectedIndicatorColor="#99ffffff" app:unSelectedIndicatorHeight="20dp" app:unSelectedIndicatorWidth="20dp"/> <com.example.zhh.mytestbanner.BannerLayout android:id="@+id/banner2" android:layout_width="match_parent" android:layout_height="180dp" android:layout_marginTop="16dp" app:defaultImage="@mipmap/ic_launcher" app:scrollDuration="1000" app:autoPlayDuration="3000" app:unSelectedIndicatorHeight="10dp" app:unSelectedIndicatorWidth="10dp" app:selectedIndicatorHeight="10dp" app:selectedIndicatorWidth="10dp" app:unSelectedIndicatorColor="#99ffffff" app:selectedIndicatorColor="#000000" app:isAutoPlay="true" /> </LinearLayout>
package com.example.zhh.mytestbanner;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.Html;
import android.widget.TextView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
BannerLayout bannerLayout1 = (BannerLayout) findViewById(R.id.banner1);
List<Integer> res = new ArrayList<>();
res.add(R.drawable.lunbo1);
res.add(R.drawable.lunbo2);
res.add(R.drawable.lunbo3);
List<String> titles = new ArrayList<>();
titles.add("標題一");
titles.add("標題二");
titles.add("標題三");
if (bannerLayout1 != null) {
bannerLayout1.setViewRes(res, titles);
}
BannerLayout bannerLayout2 = (BannerLayout) findViewById(R.id.banner2);
List<String> urls = new ArrayList<>();
urls.add("http://www.5uplus.com/image/upload/app_mall_img/7de8813b-3e41-4854-9f5a-c54b044cb21c.jpg");
urls.add("http://www.5uplus.com/image/upload/app_mall_img/58fd5357-ce41-491f-be2e-a3fe13e86d51.jpg");
urls.add("http://www.5uplus.com/image/upload/app_mall_img/5991ccc6-c125-4506-a610-4fa40e14781f.jpg");
urls.add("http://www.5uplus.com/image/upload/app_mall_img/abc67d59-65b8-4893-93ea-ff342a7fb992.jpg");
if (bannerLayout2 != null) {
bannerLayout2.setViewUrls(MainActivity.this ,urls, null);
bannerLayout2.setOnBannerItemClickListener(new BannerLayout.OnBannerItemClickListener() {
@Override
public void onItemClick(int position) {
Toast.makeText(MainActivity.this, "position:" + position, Toast.LENGTH_SHORT).show();
}
});
}
}
}
原始碼下載:
轉載地址: