This is the 24th day of my participation in the November Gwen Challenge. Check out the event details: The last Gwen Challenge 2021

In daily life, it is not difficult to find that some e-commerce and community apps will have some advertising bars in rotation. Important information can be displayed quickly through the rotation chart, so it is a common function used in App development. The rotating advertisement bar is widely used in applications. This time, when I was looking at a project of the company, I found that the function of rotating advertisement bar was used in many places, and I saw a lot of repeated codes, so I came up with the idea of integration. Let’s take a look at the style of the common round broadcast banner.

Through the above, we can also find the composition of the common round broadcast advertisement bar:

  • A ViewPager control
  • An indicator control

So in the integration process, I just wrapped the ViewPager and the indicator part. Based on the layout above, we inherit the layout from RelativeLayout. So we define class ViewPagerBarnner

Public class ViewPagerBarnner extends RelativeLayout implements OnPageChangeListener {/** * ViewPager object */ private ViewPager viewPager; /** */ private LinearLayout indicatorView; private Context context; Private List<String> imageUrls = new ArrayList<String>(); Private List<ImageView> imageViews = new ArrayList<ImageView>(); /** * Click the ImageView callback event in ViewPager */ private ViewPagerClick ViewPagerClick; /** * private float indicatorSize = 15; /** * private int idBackgroud; Private float indicatorMargin = 20; private float indicatorMargin = 20; . }Copy the code

That’s the control we need. We define the Viewpager and a LinearLayout that stores our indicators. To improve customization, we have customized three properties, as follows:

<? The XML version = "1.0" encoding = "utf-8"? > <resources> <declare-styleable name="ViewPager"> <attr name="containerHeight" format="dimension"/> <! <attr name="indicatorSize" format="dimension"/> <! <attr name="indicatorBackgroud" format="reference"/> <! <attr name="indicatorMargin" format="dimension"/> <! --> </declare-styleable> </resources>Copy the code

With a custom attribute, we get the value in the constructor. Here, we define the background as a reference type, and I pass type in the process of using it

public ViewPagerBarnner(Context context, AttributeSet attrs) { super(context, attrs); this.context = context; initViews(); TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ViewPager); indicatorSize = typedArray.getDimension(R.styleable.ViewPager_indicatorSize, 10); indicatorMargin = typedArray.getDimension(R.styleable.ViewPager_indicatorMargin, 15); idBackgroud = typedArray.getResourceId(R.styleable.ViewPager_indicatorBackgroud, 0); containerHeight = typedArray.getDimension(R.styleable.ViewPager_containerHeight, 20); typedArray.recycle(); } /** * Private void initViews(){viewPager = new viewPager (context); LayoutParams viewPagerParams = new LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT); viewPager.setLayoutParams(viewPagerParams); viewPager.setAdapter(viewPagerAdapter); viewPager.setOnPageChangeListener(this); indicatorView = new LinearLayout(context); indicatorView.setOrientation(LinearLayout.HORIZONTAL); LayoutParams layoutParams = new LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT,(int)containerHeight); layoutParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); layoutParams.addRule(RelativeLayout.CENTER_HORIZONTAL); indicatorView.setLayoutParams(layoutParams); addView(viewPager); addView(indicatorView); }Copy the code

We initialize the ViewPager and The LinearLayout and layout them at the same time. Put the LinearLayout at the bottom. The next step is to set up our listening events and the Adapter for the ViewPager.

/** * Private PagerAdapter viewPagerAdapter = new PagerAdapter() {@override public Boolean isViewFromObject(View view, Object object) { return view == object; } @Override public int getCount() { return imageUrls == null ? 0 : imageUrls.size(); } @Override public void destroyItem(ViewGroup container, int position, Object object) { container.removeView((View) object); } @Override public void finishUpdate(ViewGroup container) { } @Override public int getItemPosition(Object object) { return super.getItemPosition(object); } @Override public Object instantiateItem(ViewGroup container, int position) { ImageView imageView = null; if(imageViews ! = null && imageViews.size() > 0){ imageView = imageViews.get(position); imageView.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { if(viewPagerClick ! = null){ viewPagerClick.viewPagerOnClick(view); }}}); } container.addView(imageView); return imageView; }}; @Override public void onPageScrollStateChanged(int arg0) { } @Override public void onPageScrolled(int arg0, float arg1, int arg2) { } @Override public void onPageSelected(int location) { setSelectPage(location); }Copy the code

Now that the overall framework is complete, it’s time to assign data to the Adapter. Specific treatment is as follows:

Private void createImageView(List<String> imageUrlList){if(imageUrlList! = null && imageUrlList.size() > 0){ ImageView imageView; View pointView; for(String url : imageUrlList){ imageView = new ImageView(context); imageView.setScaleType(ScaleType.FIT_XY); ImageLoader.getInstance().displayImage(url, imageView); imageView.setTag(url); imageViews.add(imageView); pointView = new View(context); LinearLayout.LayoutParams params = new LinearLayout.LayoutParams((int)(indicatorSize),(int)(indicatorSize)); params.rightMargin = (int)indicatorMargin; pointView.setLayoutParams(params); pointView.setBackgroundDrawable(context.getResources().getDrawable(idBackgroud)); pointView.setEnabled(false); indicatorView.addView(pointView); } setSelectPage(0); Private void setSelectPage(int position){for(int index=0; index < indicatorView.getChildCount(); index++){ if(position == index){ indicatorView.getChildAt(index).setEnabled(true); }else{ indicatorView.getChildAt(index).setEnabled(false); }}} /** * Set the address of the picture, @param imageUrls */ public void addImageUrls(List<String> imageUrls) {this.imageurls.addall (imageUrls); createImageView(imageUrls); viewPagerAdapter.notifyDataSetChanged(); }Copy the code

At this point, the basic AD bar display function is completed. Let’s look at MainActivity and its use in layout.

<com.lcwang.androidviews.ViewPagerBarnner android:id="@+id/viewPager" android:layout_height="160dp" android:layout_width="match_parent" viewpager:indicatorSize="8dp" viewpager:indicatorMargin="10dp" viewpager:indicatorBackgroud="@drawable/indicator_backgroud" /> @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ViewPagerBarnner viewPagerBarnner = (ViewPagerBarnner) findViewById(R.id.viewPager); List<String> url = new ArrayList<String>(); url.add("http://pic1.nipic.com/2008-12-25/2008122510134038_2.jpg"); url.add("http://pic3.nipic.com/20090525/2416945_231841034_2.jpg"); Url. The add (" http://img3.3lian.com/2013/s1/20/d/57.jpg "); url.add("http://pic1.nipic.com/2008-11-13/2008111384358912_2.jpg"); url.add("http://img.61gequ.com/allimg/2011-4/201142614314278502.jpg"); viewPagerBarnner.addImageUrls(url); viewPagerBarnner.setViewPagerClick(new ViewPagerClick() { @Override public void viewPagerOnClick(View view) { Toast.makeText(MainActivity.this, view.getTag().toString(), Toast.LENGTH_SHORT).show(); }}); }Copy the code

We customize the style of the indicator:

<? The XML version = "1.0" encoding = "utf-8"? > <selector xmlns:android="http://schemas.android.com/apk/res/android" > <item android:state_enabled="true"> <shape android:shape="oval"> <size android:width="3dp" android:height="3dp"/> <solid android:color="#00FF00"/> </shape> </item>  <item android:state_enabled="false"> <shape android:shape="oval"> <size android:width="3dp" android:height="3dp"/> <solid android:color="#FFFFFF"/> </shape> </item> </selector>Copy the code

Through the above renderings, we can see that the basic effects are in place, but the infinite loop and auto-play are not yet implemented. So the next step is to implement infinite loops and auto-play. Let’s look at one of the two implementations that are popular on the web. I drew a sketch:

To make it easier to add headers and tails, I changed the collection that holds urls and ImageViews to LinkedList.

	private LinkedList<String> imageUrls = new LinkedList<String>();
	private LinkedList<ImageView> imageViews = new LinkedList<ImageView>();
Copy the code

Major judgment process is to increase the head end, said some here, the increase in the collection for the ImageView in fore and aft, why to get instead of using the already existing in the collection, this is because if we will be using the object, after holding the object reference, and then in the switch, submitted to the ImageView has a parent container. (That’s what I understand)

@Override public void onPageSelected(int location) { if(location == this.imageUrls.size() -1){ location = 1; viewPager.setCurrentItem(location,false); }else if(location == 0){ location = this.imageUrls.size() -2; viewPager.setCurrentItem(location,false); } currentPostion = location; setSelectPage(location - 1); } /** * Private void createImageView(List<String> imageUrlList){if(imageUrlList! = null && imageUrlList.size() > 0){ ImageView imageView; View pointView; If (imageviews.size () > 1){imageviews.removeFirst (); imageViews.removeLast(); } for(String url : imageUrlList){ imageView = new ImageView(context); imageView.setScaleType(ScaleType.FIT_XY); ImageLoader.getInstance().displayImage(url, imageView); imageView.setTag(url); imageViews.add(imageView); pointView = new View(context); LinearLayout.LayoutParams params = new LinearLayout.LayoutParams((int)(indicatorSize),(int)(indicatorSize)); params.rightMargin = (int)indicatorMargin; pointView.setLayoutParams(params); pointView.setBackgroundDrawable(context.getResources().getDrawable(idBackgroud)); pointView.setEnabled(false); indicatorView.addView(pointView); } if(imageviews.size () > 1){ImageView ivFirst = new ImageView(context); ImageView ivLast = new ImageView(context); ImageLoader.getInstance().displayImage(imageUrls.getLast(),ivLast); ImageLoader.getInstance().displayImage(imageUrls.getFirst(),ivFirst); imageViews.addFirst(ivFirst); imageViews.addLast(ivLast); } viewPagerAdapter.notifyDataSetChanged(); viewPager.setCurrentItem(1); Private void setSelectPage(int position){for(int index=0; index < indicatorView.getChildCount(); index++){ if(position == index){ indicatorView.getChildAt(index).setEnabled(true); }else{ indicatorView.getChildAt(index).setEnabled(false); }}} /** * Set the address of the picture, @param imageUrls */ public void addImageUrls(List<String> imageUrls) {if(this.imageurls.size () > 1){// Clear the header and the header this.imageUrls.removeFirst(); this.imageUrls.removeLast(); } this.imageUrls.addAll(imageUrls); If (this.imageurls.size () >1){// Add first String first = this.imageurls.getFirst (); this.imageUrls.addFirst(this.imageUrls.getLast()); this.imageUrls.addLast(first); } createImageView(imageUrls); }Copy the code

This completes the ViewPager loop, and we can add pages using the addImageUrls method. Once the loop is realized, it starts to play automatically. There are no more than several kinds of auto-play implementations:

  • 1. Timer: Timer
  • 2, open the child thread while true loop
  • 3, ColckManager
  • 4. Use handler to send delay information and realize the loop

We do this with a Handler.

private Handler mHandler = new Handler(){ @Override public void handleMessage(Message msg) { viewPager.setCurrentItem(currentPostion + 1); mHandler.sendEmptyMessageDelayed(0, 3000); }}; Private void initViews(){private void initViews(){private void initViews(){ mHandler.sendEmptyMessageDelayed(0, 3000); }Copy the code

This completes the development of a composite ViewPager. To get a glimpse of the effect:

At this point, the control is complete, can be very easy to use, also do not have to do a lot of logical judgment. This inside load network images using ImageLoader library, need to add, or change the source code to modify their own.