亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 系統 > Android > 正文

實現輪轉廣告帶底部指示的自定義ViewPager控件

2020-04-11 12:00:00
字體:
來源:轉載
供稿:網友

有許多博客和開源項目都致力于這項工作,但是他們的工作大都是為了制作類似于啟動頁的效果,ViewPager全屏顯示,或者自己可操作的屬性難以滿足要求,因此我想把ViewPager和底部的指示物封裝在一個自定義的View中,作為一個新的控件在xml中使用,所以自己來實現了一個。
而且,在用自定義視圖封裝ViewPager時,出現了一個問題,就是ViewPager的所有頁不能全部顯示的問題,不知道是因為這個問題太簡單還是什么其它原因,在網上并沒有搜到這個問題的解決方法(事實上連提問的人都沒有……),困擾了我半個多星期,終于解決,這一點在正文里會介紹,先來貼一下效果圖:

下面來介紹我的實現過程:

首先在res/values/目錄下創建attrs.xml文件,用來定義新View自定義的屬性:

復制代碼 代碼如下:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <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" />
    </declare-styleable>
</resources>

其中:

dotsViewHeight定義底部指示物所在視圖(我定義為一個LinearLayout)的高度,也就是示例圖中圓圈所在灰色透明部分的高度,默認為40像素;

dotsSpacing定義底部指示物之間的間距,默認為0;

dotsFocusImage定義代表當前頁的指示物的樣子;

dotsBlurImage定義代表非當前頁的指示物的樣子;

android:scaleType定義ViewPager中ImageView的scale類型,如果ViewPager中的View不是ImageView,則此屬性沒有效果,默認為ScaleType.FIT_XY;

android:gravity定義底部指示物在父View(即示例灰色透明部分)的gravity屬性;

dotsBackground定義底部指示物的背景顏色或背景圖;

dotsBgAlpha定義底部指示物的背景顏色或背景圖的透明度,取值為0-1,0代表透明;

changeInteval定義ViewPager自動切換的時間間隔,單位為ms,默認為1000ms(這個地方實際的間隔比設置的要大,不知道是什么原因,望高手解答);

下一步,定義PageAdapter,為ViewPager提供內容:

復制代碼 代碼如下:

public class ViewPagerAdapter extends PagerAdapter {

    private List<View> views = null;
    private ScaleType scaleType;

    public ViewPagerAdapter(List<View> views) {
        this(views, ScaleType.CENTER);
    }

    public ViewPagerAdapter(List<View> views, ScaleType scaleType) {
        super();
        this.views = views;
        this.scaleType = scaleType;
    }

定義一個views來存儲要顯示的View,然后定義一個ScaleType來規定如果ViewPager是用來顯示ImageView的,ImageView應該怎樣呈現在ViewPager當中,如果調用的構造函數不傳ScaleType信息,則默認使用ScaleType.CENTER。
根據官方API描述,需要重寫PageAdapter的getCount,isViewFromObject,instantiateItem和destroyItem這四個方法,在instantiateItem中設置ScaleType,其它幾個方法,都是用官方描述的寫法,沒有做什么新的改動:

復制代碼 代碼如下:

@Override
public int getCount() {
    // TODO Auto-generated method stub
    return views.size();
}

@Override
public boolean isViewFromObject(View arg0, Object arg1) {
    // TODO Auto-generated method stub
    return arg0 == arg1;
}

@Override
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;
}

@Override
public void destroyItem(View container, int position, Object object) {
    // TODO Auto-generated method stub
    ((ViewPager) container).removeView((View) object);
}

下面就是重頭戲了,核心類,被封裝的底部帶指示物的ViewPager,基本思路是自定義一個類繼承LinearLayout,在里面加入兩個子視圖ViewPager和LinearLayout(放置指示物),并且,因為要定期輪轉,還實現了Runnable接口,定義了以下的變量:

復制代碼 代碼如下:

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是要顯示的ViewPager對象,viewDots是放置指示物的子視圖,dots是viewDots上的指示物項,views是ViewPager項,position指示當前正在顯示第幾張圖,isContinue表示可不可以自動輪轉(當手指觸摸時不輪轉),在下面的就是雨attrs.xml中定義的屬性相對應的值。作為一個能夠在xml布局文件中直接使用的View,必須重寫擁有Context和AttributeSet參數的構造函數:

復制代碼 代碼如下:

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);
            //這里依次獲取所有的屬性值,此處省略,可參看最后附上的全部代碼
        } finally {
            a.recycle();
        }

    initView();
}

最后調用的函數initView,用來初始化ViewPager和LinearLayout這兩個子視圖,同時,如果xml中給指示物設置了背景,在這里進行設置:

復制代碼 代碼如下:

@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,
            LayoutParams.MATCH_PARENT);
    addView(viewPager, lp);
    if (dotsBackground != null) {
        dotsBackground.setAlpha((int) (dotsBgAlpha * 255));
        viewDots.setBackground(dotsBackground);
    }
    viewDots.setGravity(gravity);
    addView(viewDots, lp);
}


使用這個類時,關鍵就是創建一個List<View>,并作為參數傳進來供ViewPager(PagerAdapter)使用,對外的接口就是這個setViewPagerViews:

復制代碼 代碼如下:

public void setViewPagerViews(List<View> views) {
    this.views = views;
    addDots(views.size());

    viewPager.setAdapter(new ViewPagerAdapter(views, scaleType));

    viewPager.setOnPageChangeListener(new OnPageChangeListener() {
        @Override
        public void onPageSelected(int index) {
            // TODO Auto-generated method stub
            position = index;
            switchToDot(index);
        }
        //override的兩個空方法,此處省略
    });

    viewPager.setOnTouchListener(new OnTouchListener() {

        @Override
        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;
                break;
            case MotionEvent.ACTION_UP:
                isContinue = true;
                break;
            default:
                isContinue = true;
                break;
            }
            return false;
        }
    });
    new Thread(this).start();
}

addDots就是在底部添加多少個小點,默認第一個處于被選中狀態,關鍵是OnPageChangeListener的onPageSelected方法,這個方法在viewPager進行切換時調用,做的工作就是把底部的指示物切換到對應的標識上,在這個方法的最后,啟動了輪轉的線程。

復制代碼 代碼如下:

@Override
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() {
    @Override
    public void handleMessage(Message msg) {
        // TODO Auto-generated method stub
        viewPager.setCurrentItem(msg.what);
        super.handleMessage(msg);
    }
};

在這個線程中,每隔固定秒數,就向Handler隊列中發送一個消息,內容就是要顯示的view項的index,然后再handler中調用viewPager的setCurrentItem方法進行跳轉。至此,最核心的類就完成了,但還剩很關鍵的一個方法,作為一個自定義的View,要重寫父類的onLayout方法來對子元素進行布局,就是這一個方法中不當的代碼,導致每次只能顯示前兩張圖,因為ViewPager在顯示時,會默認初始化當前頁和前后頁,對于第一張來說,沒有前一頁,所以初始化了兩張,在ViewPager滑動時,每次都會調用onLayout方法,而且,changed參數為false,我已開始只判斷changed為true時才進行布局,就造成了上述問題,完整的onLayout代碼如下:

復制代碼 代碼如下:

@Override
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());
    }
}

最后,就是如何使用這個類了,首先,在activity的布局文件中聲明這個組件:

復制代碼 代碼如下:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:daemon="http://schemas.android.com/apk/res/org.daemon.viewpager"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#666666" >

    <org.daemon.viewpager.MyViewPager
        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" />

</RelativeLayout>

然后,在MainActivity中,創建List<View>數組并設置數據:

復制代碼 代碼如下:

@Override
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(R.id.my_view_pager);
    pager.setViewPagerViews(views);
}

至此,本示例就全部講解完了,兩個問題,一個就是為什么使用Thread的方法來控制時間間隔,實際值會比設置的值長,是因為Message在排隊嗎,第二個問題,就是為什么ViewPager滑動時不重新對ViewPager布局,就會不顯示任何圖,這兩個問題還有待大家解答。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
91精品国产沙发| 欧美日韩综合视频网址| 日韩中文字幕精品| 国产精品69久久久久| 91亚洲一区精品| x99av成人免费| 亚洲自拍欧美另类| 日韩精品极品视频免费观看| 在线看福利67194| 久久人人看视频| 日韩高清免费观看| 久久精品视频免费播放| 三级精品视频久久久久| 欧美激情欧美狂野欧美精品| 97视频国产在线| 成人有码在线播放| 亚洲精品在线观看www| 欧美激情视频网址| 日韩人在线观看| 韩曰欧美视频免费观看| 国产精品久久久久久久久免费| 成人免费视频在线观看超级碰| 在线观看日韩av| www.久久撸.com| 精品亚洲精品福利线在观看| 国产69久久精品成人看| 成人激情电影一区二区| 91久久夜色精品国产网站| 久久99精品久久久久久琪琪| 国产日产欧美a一级在线| 欧美综合在线观看| 日韩av资源在线播放| 欧美精品免费在线观看| 黄色成人在线免费| 国产99久久精品一区二区 夜夜躁日日躁| 亚洲男人第一网站| 91午夜理伦私人影院| 亚洲成人黄色在线| 欧美专区在线视频| 91精品国产乱码久久久久久久久| 91欧美日韩一区| 最近的2019中文字幕免费一页| 欧美大肥婆大肥bbbbb| 久久夜色精品亚洲噜噜国产mv| 国产不卡在线观看| 欧美一级免费视频| 欧美裸身视频免费观看| 日韩中文字幕在线免费观看| 三级精品视频久久久久| 久久久亚洲天堂| 国产一区二区三区网站| 欧美国产日韩免费| 欧美日韩亚洲国产一区| 久久精品男人天堂| 最近2019好看的中文字幕免费| 成人中心免费视频| 中文日韩在线视频| 国产在线拍揄自揄视频不卡99| 国产亚洲a∨片在线观看| 福利视频导航一区| 欧美日韩xxxxx| 国产91成人在在线播放| 色诱女教师一区二区三区| 久久天天躁夜夜躁狠狠躁2022| 国产一区二区动漫| 亚洲精品一区在线观看香蕉| 国产精品v片在线观看不卡| 欧美中文在线观看国产| 欧美视频在线观看 亚洲欧| 精品国产乱码久久久久久虫虫漫画| 国产精品国模在线| 欧美在线观看一区二区三区| 精品激情国产视频| 国产精品一香蕉国产线看观看| 97视频在线观看亚洲| 欧美裸身视频免费观看| 日韩资源在线观看| 91中文字幕在线| 精品magnet| 亚洲夜晚福利在线观看| 精品久久中文字幕| 九九精品在线视频| 97香蕉久久超级碰碰高清版| 亚洲香蕉成视频在线观看| 色多多国产成人永久免费网站| 日韩精品免费在线观看| 国产精品网站入口| 欧美色视频日本高清在线观看| 亚洲天堂av高清| 5252色成人免费视频| 亚洲最大av网站| 一色桃子一区二区| 日本不卡免费高清视频| 97色在线观看免费视频| 亚洲一区二区三区777| 久久久久久久影院| 欧美性猛交xxxx乱大交| 国产成人在线精品| 97在线观看免费高清| 91国自产精品中文字幕亚洲| 国产成人亚洲综合91| 北条麻妃一区二区三区中文字幕| 久久影院在线观看| 神马久久久久久| 青青a在线精品免费观看| 九九热这里只有在线精品视| 国产精品久久久久久久久久三级| 日韩成人在线视频网站| 性欧美办公室18xxxxhd| 日韩女优人人人人射在线视频| 亚洲黄色www| 日韩欧美在线免费观看| 97精品国产97久久久久久免费| 亚洲一区二区精品| 日本一区二区不卡| 欧美成人性色生活仑片| 日本在线观看天堂男亚洲| 亚洲精品二三区| 97超级碰碰碰| 午夜精品福利电影| 亚洲电影免费观看高清完整版在线| 91精品国产一区| 国产精品久久一区| 欧美巨大黑人极品精男| 96精品视频在线| 最近2019中文字幕mv免费看| 亚洲一区二区三区在线免费观看| 国产91亚洲精品| 亚州av一区二区| 国产日韩av在线播放| 亚洲欧美精品一区二区| 欧美黄色片在线观看| 亚洲欧美变态国产另类| 国产精品三级在线| 亚洲人成伊人成综合网久久久| 亚洲国产一区二区三区在线观看| 欧美激情免费视频| 中文字幕日韩精品有码视频| 国产精品a久久久久久| 久久精品中文字幕| 国产精品日韩欧美大师| 国产欧美日韩免费| 91麻豆国产语对白在线观看| 精品亚洲男同gayvideo网站| 精品国产91乱高清在线观看| 国产一区二区日韩| 国产精品成人在线| 久久国产精品久久久| 国产精品老牛影院在线观看| 欧美亚洲激情在线| 日韩欧美在线视频免费观看| 欧美成人性生活| 亚洲国产高清福利视频| 久久久久久久久中文字幕| 97视频在线免费观看| 亚洲精品影视在线观看| 午夜精品福利视频| 97香蕉超级碰碰久久免费的优势| 国产精品久久久久久五月尺| 日韩精品极品毛片系列视频| 日本精品久久久久久久| 97人人模人人爽人人喊中文字| 欧美第一黄网免费网站| 伊人精品在线观看|