Implement a custom Viewpager control that is indicated at the bottom of the Rotary ad band _android

Source: Internet
Author: User
Tags object object stub time interval

There are a lot of blogs and open source projects that are dedicated to this work, but most of their work is done to create a page-like effect, viewpager full screen, or the properties they can manipulate are difficult to meet, so I want to encapsulate the viewpager and bottom indicators in a custom view, As a new control is used in XML, so you implement one yourself.
Also, when you encapsulate Viewpager with a custom view, there is a problem, that is, the viewpager of all pages can not be fully displayed, I do not know whether it is because the problem is too simple or what other reasons, on the internet did not find the solution to the problem (in fact, not even the people asking questions ...). , bothered me for more than half a week, finally resolved, this point in the text will be introduced, first to paste the effect of the picture:

Here is a description of my implementation process:

First, create a attrs.xml file in the res/values/directory to define the properties for the new view customization:

Copy Code code as follows:

<?xml version= "1.0" encoding= "Utf-8"?>
<declare-styleable name= "Myviewpager" >
<attr name= "Dotsviewheight" format= "Dimension"/>
<attr name= "dotsspacing" format= "Dimension"/>
<attr name= "Dotsfocusimage" format= "Reference"/>
<attr name= "Dotsblurimage" format= "Reference"/>
<attr name= "Android:scaletype"/>
<attr name= "Android:gravity"/>
<attr name= "Dotsbackground" format= "Reference|color"/>
<attr name= "Dotsbgalpha" format= "float"/>
<attr name= "Changeinterval" format= "integer"/>


Dotsviewheight defines the height of the view in which the bottom indicator resides (which I define as a linearlayout), which is the height of the gray transparent portion of the circle in the example diagram, which defaults to 40 pixels;

Dotsspacing defines the spacing between the bottom indicators, which defaults to 0;

Dotsfocusimage defines the appearance of the indicator representing the current page;

Dotsblurimage defines the appearance of an indicator that represents a non-current page;

Android:scaletype defines the scale type of ImageView in Viewpager, and if the view in Viewpager is not ImageView, this property has no effect, and the default is Scaletype.fit_xy;

Android:gravity defines the gravity attribute of the bottom indicator in the parent view (that is, the example gray transparent part);

Dotsbackground defines the background color or background image of the bottom indicator;

Dotsbgalpha defines the transparency of the background color or background image of the bottom indicator, with a value of 0-1, and 0 for transparency;

Changeinteval define viewpager Automatic switch time interval, unit for MS, default to 1000MS (this place actual interval than set to big, do not know what the reason, Hope Master answer);

Next, define PageAdapter and provide content for Viewpager:

Copy Code code as follows:

public class Viewpageradapter extends Pageradapter {

Private list<view> views = null;
Private ScaleType ScaleType;

Public Viewpageradapter (list<view> views) {
This is (views,;

Public Viewpageradapter (list<view> views, ScaleType ScaleType) {
Super ();
This.views = views;
This.scaletype = ScaleType;

Define a views to store the view to display, and then define a scaletype to specify how ImageView should appear in Viewpager if the Viewpager is used to display ImageView. If the invoked constructor does not pass ScaleType information, the is used by default.
According to the official API description, you need to rewrite the four methods of PageAdapter's Getcount,isviewfromobject,instantiateitem and Destroyitem, In the Instantiateitem set of ScaleType, several other methods, are written in the official description, did not make any new changes:

Copy Code code as follows:

public int GetCount () {
TODO auto-generated Method Stub
return Views.size ();

public boolean isviewfromobject (View arg0, Object arg1) {
TODO auto-generated Method Stub
return arg0 = = Arg1;

Public Object Instantiateitem (View container, int position) {
TODO auto-generated Method Stub
View view = Views.get (position);
Viewpager Viewpager = (viewpager) container;
if (view instanceof ImageView) {
((ImageView) view). Setscaletype (ScaleType);
Viewpager.addview (view, 0);
return view;

public void Destroyitem (View container, int position, object object) {
TODO auto-generated Method Stub
((Viewpager) container). Removeview ((View) object);

Here is the play, the core class, the encapsulated bottom with the Viewpager, the basic idea is to customize a class to inherit LinearLayout, add two sub views inside Viewpager and linearlayout (drop indicator), and, because to rotate periodically , the Runnable interface is implemented and the following variables are defined:

Copy Code code as follows:

public class Myviewpager extends LinearLayout implements Runnable {

Private Viewpager Viewpager;
Private LinearLayout viewdots;
Private list<imageview> dots;
Private list<view> views;

private int position = 0;
Private Boolean iscontinue = true;

private float dotsviewheight;
private float dotsspacing;
Private drawable Dotsfocusimage;
Private drawable Dotsblurimage;
Private ScaleType ScaleType;
private int gravity;
Private drawable Dotsbackground;
private float Dotsbgalpha;
private int changeinterval;

Viewpager is the Viewpager object to display, Viewdots is the child view of the drop indicator, dots is the indicator on the viewdots, views are viewpager items, position indicates that the first few pictures are currently being displayed, Iscontinue said it can be automatic rotation (when the fingers touch the rotation), in the following is the rain attrs.xml defined in the corresponding value of the attribute. As a view that can be used directly in an XML layout file, you must override the constructor that owns the context and AttributeSet parameters:

Copy Code code as follows:

Public Myviewpager (context context, AttributeSet Attrs) {
Super (context, attrs);
TODO auto-generated Constructor stub
TypedArray a = Context.obtainstyledattributes (Attrs,
R.styleable.myviewpager, 0, 0);

try {
Dotsviewheight = A.getdimension (
R.styleable.myviewpager_dotsviewheight, 40);
This gets all the attribute values in turn, omitted here, see the last attached code.
finally {
A.recycle ();

Initview ();

The last function called Initview is used to initialize the two child views of Viewpager and LinearLayout, and if the background is set in XML, set here:

Copy Code code as follows:

@SuppressLint ("Newapi")
private void Initview () {
TODO auto-generated Method Stub
Viewpager = new Viewpager (GetContext ());
Viewdots = new LinearLayout (GetContext ());

Layoutparams LP = new Layoutparams (layoutparams.match_parent,
AddView (Viewpager, LP);
if (dotsbackground!= null) {
Dotsbackground.setalpha ((int) (DOTSBGALPHA * 255));
Viewdots.setbackground (Dotsbackground);
Viewdots.setgravity (Gravity);
AddView (viewdots, LP);

The key to using this class is to create a list<view>, and pass it in as a parameter for Viewpager (pageradapter), the external interface is this setviewpagerviews:

Copy Code code as follows:

public void Setviewpagerviews (list<view> views) {
This.views = views;
Adddots (Views.size ());

Viewpager.setadapter (New Viewpageradapter (views, scaletype));

Viewpager.setonpagechangelistener (New Onpagechangelistener () {
public void onpageselected (int index) {
TODO auto-generated Method Stub
Position = index;
Switchtodot (index);
Override two null method, omitted here

Viewpager.setontouchlistener (New Ontouchlistener () {

public Boolean Ontouch (view view, Motionevent motionevent) {
TODO auto-generated Method Stub
Switch (motionevent.getaction ()) {
Case Motionevent.action_down:
Case Motionevent.action_move:
Iscontinue = false;
Iscontinue = true;
Iscontinue = true;
return false;
New Thread (This). Start ();

Adddots is the number of small points added at the bottom, the default is the first in the selected state, the key is the Onpagechangelistener onpageselected method, which is called when the Viewpager switch, The job is to switch the indicator at the bottom to the corresponding logo, and at the end of the method, start the thread of rotation.

Copy Code code as follows:

public void Run () {
TODO auto-generated Method Stub
while (true) {
if (iscontinue) {
Pagehandler.sendemptymessage (position);
Position = (position + 1)% views.size ();
try {
Thread.Sleep (Changeinterval);
catch (Interruptedexception e) {
TODO auto-generated Catch block
E.printstacktrace ();

Handler Pagehandler = new Handler () {
public void Handlemessage (msg) {
TODO auto-generated Method Stub
Viewpager.setcurrentitem (Msg.what);
Super.handlemessage (msg);

In this thread, every fixed number of seconds sends a message to the handler queue, which is the index of the view item to display, and then handler the Viewpager Setcurrentitem method to jump. At this point, the core of the class is complete, but there is a critical way, as a custom view, to override the parent class's OnLayout method to lay out the child element, which is the wrong code in this method, which results in the first two graphs at a time, because when Viewpager is displayed, Initializes the current page and the front and back pages by default, and for the first one, there is no previous page, so two are initialized, and the OnLayout method is invoked each time the Viewpager slide, and the changed argument is false. I have started to judge only when changed is true to layout, causing the above problem, the complete onlayout code is as follows:

Copy Code code as follows:

protected void OnLayout (Boolean changed, int l, int t, int r, int b) {
TODO auto-generated Method Stub
View child = This.getchildat (0);
Child.layout (0, 0, getwidth (), getheight ());

if (changed) {
Child = This.getchildat (1);
Child.measure (r-l, (int) dotsviewheight);
Child.layout (0, GetHeight ()-(int) dotsviewheight, getwidth (),
GetHeight ());

Finally, how to use this class, first, declare the component in the activity's layout file:

Copy Code code as follows:

<relativelayout xmlns:android= "Http://"
xmlns:daemon= "Http://"
Android:layout_width= "Match_parent"
android:layout_height= "Match_parent"
android:background= "#666666" >

        Android : id= "@+id/my_view_pager"
        android:layout_width= "Match_parent"
        android:layout_height= "200DP"
         daemon:dotsviewheight= "30DP"
        daemon:dotsfocusimage= "@ drawable/dot_focused "
        daemon:dotsblurimage=" @drawable/dot_normal "
        daemon:dotsspacing= "5DP"
         daemon:dotsbackground= "#999999"
        daemon:dotsbgalpha= "0.5"
        daemon:changeinterval= "3000"
         android:scaletype= "Fitxy"
        android:gravity= "center"/ "


Then, in Mainactivity, create the list<view> array and set the data:

Copy Code code as follows:

protected void OnCreate (Bundle savedinstancestate) {
Super.oncreate (savedinstancestate);
Setcontentview (R.layout.activity_main);
Initviewpager ();

private void Initviewpager () {
views = new arraylist<view> ();

ImageView image = New ImageView (this);
Image.setimageresource (R.drawable.demo_scroll_image);
Views.add (image);
Image = New ImageView (this);
Image.setimageresource (R.drawable.demo_scroll_image2);
Views.add (image);
Image = New ImageView (this);
Image.setimageresource (R.drawable.demo_coupon_image);
Views.add (image);
Image = New ImageView (this);
Image.setimageresource (R.drawable.demo_scroll_image2);
Views.add (image);

Myviewpager pager = (Myviewpager) Findviewbyid (;
Pager.setviewpagerviews (views);

At this point, this example is all explained, two questions, one is why use the thread method to control the time interval, the actual value will be longer than the set value, because the message is queued, the second question, is why Viewpager when the Viewpager layout is not reset, will not show any diagram, these two questions remain to be answered.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.