Android之最炫的旋转引导页

 

     每个APP都会有一个自己的引导页,下面我就分享一个最炫的旋转的引导页效果:

一.效果一

1.效果图:

2.主函数代码:

import android.os.Build;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.ImageView;

import com.hjq.demo.R;
import com.hjq.demo.ui.activity.sgf.sgfbanner.ZiDingYiBannerActivity;
import com.hjq.demo.ui.activity.sgf.sgfbanner.banner.BannerEntry;
import com.hjq.demo.ui.activity.sgf.sgfbanner.banner.SimpleBannerEntry;
import com.hjq.demo.ui.activity.sgf.sgfbanner.banner.view.BannerView;
import com.hjq.demo.ui.activity.sgf.sgfbanner.transformer.AlphaPageTransformer;
import com.hjq.demo.ui.activity.sgf.sgfbanner.transformer.CardPageTransformer;

import java.util.ArrayList;
import java.util.List;

import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.view.ViewCompat;
import androidx.core.view.ViewPropertyAnimatorListener;

/**
 * **描述:** 利用BannerView实现引导页。
 * <p>
 * **创建人:** kelin
 * <p>
 * **创建时间:** 2019-11-19  14:02
 * <p>
 * **版本:** v 1.0.0
 */
public class GuideActivity extends AppCompatActivity {

    private List<GuidePageEntry> guideEntries = new ArrayList<>(3);

    private View btnExperienceNow;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
            getWindow().getAttributes().layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
        }
        //当系统版本为4.4或者4.4以上时可以使用沉浸式状态栏
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            //透明状态栏
            getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
        }
        setContentView(R.layout.activity_guide);
        btnExperienceNow = findViewById(R.id.btnExperienceNow);
        guideEntries.add(new GuidePageEntry(R.drawable.guide_p1_img, R.drawable.guide_p1_word));
        guideEntries.add(new GuidePageEntry(R.drawable.guide_p2_img, R.drawable.guide_p2_word));
        guideEntries.add(new GuidePageEntry(R.drawable.guide_p3_img, R.drawable.guide_p3_word));

        btnExperienceNow.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                ZiDingYiBannerActivity.start(GuideActivity.this);
                finish();
            }
        });

        BannerView bvGuide = findViewById(R.id.bvGuide);
//        bvGuide.setPageTransformer(true, new AlphaPageTransformer());
        bvGuide.setPageTransformer(true, new CardPageTransformer());
        bvGuide.setEntries(guideEntries, false);
        bvGuide.setOnPageChangedListener(new BannerView.OnPageChangeListener() {
            @Override
            public void onPageSelected(BannerEntry entry, int index) {
                switch (index) {
                    case 2:
                        btnExperienceNow.setEnabled(true);
                        ViewCompat.animate(btnExperienceNow).alpha(1.0F)
                                .setDuration(500).setListener(new ViewPropertyAnimatorListener() {
                            @Override
                            public void onAnimationStart(View view) {
                                btnExperienceNow.setVisibility(View.VISIBLE);
                            }

                            @Override
                            public void onAnimationEnd(View view) {
                                btnExperienceNow.setVisibility(View.VISIBLE);
                            }

                            @Override
                            public void onAnimationCancel(View view) {

                            }
                        }).start();
                        btnExperienceNow.setVisibility(View.VISIBLE);
                        break;
                    case 1:
                        btnExperienceNow.setEnabled(false);
                        ViewCompat.animate(btnExperienceNow).alpha(0F)
                                .setDuration(500)
                                .setListener(new ViewPropertyAnimatorListener() {
                                    @Override
                                    public void onAnimationStart(View view) {

                                    }

                                    @Override
                                    public void onAnimationEnd(View view) {
                                        btnExperienceNow.setVisibility(View.INVISIBLE);
                                    }

                                    @Override
                                    public void onAnimationCancel(View view) {

                                    }
                                }).start();
                        float alpha = btnExperienceNow.getAlpha();
                        if (alpha != 0f) {
                            btnExperienceNow.setAlpha(0F);
                        }
                        break;
                    default:
                        float a = btnExperienceNow.getAlpha();
                        btnExperienceNow.setVisibility(View.INVISIBLE);
                        if (a != 0f) {
                            btnExperienceNow.setAlpha(0F);
                        }
                }
            }

            @Override
            public void onPageScrolled(int index, float positionOffset, int positionOffsetPixels) {

            }

            @Override
            public void onPageScrollStateChanged(int state) {

            }
        });
    }


    public class GuidePageEntry extends SimpleBannerEntry<String> {
        private int imageRes;
        private int wordsRes;

        /**
         * BannerEntry构造器。
         */
        GuidePageEntry(@DrawableRes int imageRes, @DrawableRes int wordsRes) {
            super("");
            this.imageRes = imageRes;
            this.wordsRes = wordsRes;
        }

        @NonNull
        @Override
        public View onCreateView(ViewGroup parent) {
            View rootView = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_guide_page, parent, false);
            ((ImageView) rootView.findViewById(R.id.ivImage)).setImageResource(imageRes);
            ((ImageView) rootView.findViewById(R.id.ivWords)).setImageResource(wordsRes);
            return rootView;
        }
    }
}

 3.动画效果代码:


import android.annotation.SuppressLint;
import android.content.Context;
import android.util.TypedValue;
import android.view.View;

import androidx.viewpager.widget.ViewPager;

/**
 * **描述:** 缩放动画
 */
public class CardPageTransformer implements ViewPager.PageTransformer {
    /**
     * 偏移量
     */
    private int mScaleOffset;

    public CardPageTransformer() {
        this(40);
    }

    /**
     * @param mScaleOffset 缩放偏移量 单位 px
     */
    public CardPageTransformer(int mScaleOffset) {
        this.mScaleOffset = mScaleOffset;
    }

    @SuppressLint("NewApi")
    public void transformPage(View page, float position) {
        if (position < -1) {
            page.setTranslationX(0);
            page.setRotation(0);
        } else if (position <= 0.0f) {//被滑动的那页 position 是-下标~ 0
            //旋转角度 45° * -0.1 = -4.5°
            page.setRotation((45 * position));
            //X轴偏移 li: 300/3 * -0.1 = -10
            page.setTranslationX((page.getWidth() / 3 * position));
        } else {
            //缩放比例
            page.setTranslationX(0);
            page.setRotation(0);
            float scale = (page.getWidth() - mScaleOffset * position) / (float) (page.getWidth());
            page.setScaleX(scale);
            page.setScaleY(scale);
            page.setTranslationX((-page.getWidth() * position));
            page.setTranslationY((mScaleOffset * 0.8f) * position);
        }
    }
}

 

import android.view.View;

import androidx.viewpager.widget.ViewPager;

/**
 * 描述 透明转场动画。
 */

public class AlphaPageTransformer implements ViewPager.PageTransformer {

    public void transformPage(View view, float position) {
        int pageWidth = view.getWidth();

        if (position < -1) { // [-Infinity,-1)
            // This page is way off-screen to the left.
            view.setAlpha(0);
        } else if (position <= 0) { // [-1,0]
            // Use the default slide transition when moving to the left page
            view.setAlpha(1 + position);
            view.setTranslationX(pageWidth * -position);
        } else if (position <= 1) { // (0,1]
            // Fade the page out.
            view.setAlpha(1 - position);

            // Counteract the default slide transition
            view.setTranslationX(pageWidth * -position);

        } else { // (1,+Infinity]
            view.setAlpha(0);
        }
    }
}

 

import android.view.View;

import androidx.viewpager.widget.ViewPager;

/**
 * 描述 ViewPager的翻页动画,这里的代码是从Google官方的文档中拷贝过来的。
 * 创建人 kelin
 * 创建时间 2017/7/25  下午5:47
 * 版本 v 1.0.0
 */

public class DepthPageTransformer implements ViewPager.PageTransformer {
    private static final float MIN_SCALE = 0.75f;

    public void transformPage(View view, float position) {
        int pageWidth = view.getWidth();

        if (position < -1) { // [-Infinity,-1)
            // This page is way off-screen to the left.
            view.setAlpha(0);

        } else if (position <= 0) { // [-1,0]
            // Use the default slide transition when moving to the left page
            view.setAlpha(1);
            view.setTranslationX(0);
            view.setScaleX(1);
            view.setScaleY(1);

        } else if (position <= 1) { // (0,1]
            // Fade the page out.
            view.setAlpha(1 - position);

            // Counteract the default slide transition
            view.setTranslationX(pageWidth * -position);

            // Scale the page down (between MIN_SCALE and 1)
            float scaleFactor = MIN_SCALE
                    + (1 - MIN_SCALE) * (1 - Math.abs(position));
            view.setScaleX(scaleFactor);
            view.setScaleY(scaleFactor);

        } else { // (1,+Infinity]
            // This page is way off-screen to the right.
            view.setAlpha(0);
        }
    }
}

import android.annotation.SuppressLint;
import android.os.Build;
import android.view.View;

import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.viewpager.widget.ViewPager;

/**
 * **描述:** 立体效果的Transformer。
 * <p>
 * **创建人:** kelin
 * <p>
 * **创建时间:** 2019-08-28  16:26
 * <p>
 * **版本:** v 1.0.0
 */
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public class GalleryTransformer implements ViewPager.PageTransformer {

    @SuppressLint("NewApi")
    @Override
    public void transformPage(@NonNull View page, float position) {
        float scaleValue = 1 - Math.abs(position) * 0.3F;
        page.setScaleX(scaleValue);
        page.setScaleY(scaleValue);
        page.setAlpha(scaleValue);
        //设置缩放的x轴中心点
        page.setPivotX(page.getWidth() * (1 - position - (position > 0 ? 1 : -1) * 0.75f) * 0.5f);
        //设置缩放的y轴中心点
        page.setPivotY(page.getHeight() / 2.0F);
        page.setElevation(position > -0.25 && position < 0.25 ? 1 : 0);
    }
}
import android.view.View;
import android.view.ViewGroup;

import androidx.annotation.IntRange;
import androidx.viewpager.widget.ViewPager;

/**
 * <strong>描述: </strong> 卷轴画效果的翻页动画
 * <p><strong>创建人: </strong> kelin
 * <p><strong>创建时间: </strong> 2018/3/28  下午2:42
 * <p><strong>版本: </strong> v 1.0.0
 */

public class ScrollPaintingPageTransformer implements ViewPager.PageTransformer {

    private final int factor;

    /**
     * 构造一个卷轴画效果的PageTransformer。
     */
    public ScrollPaintingPageTransformer() {
        this(1);
    }

    /**
     * 构造一个卷轴画效果的PageTransformer。
     * @param factor 该参数决定了前后两页所重叠的部分为多少,如果不希望前后两页有所重叠,那么该参数的值应当为1。
     *               如果你希望重叠的部分占整个页面的一半(也就是二分之一)的话那么该参数的值应当为2。其实你可以把
     *               该参数看成是分母,分子永远为1。
     */
    public ScrollPaintingPageTransformer(@IntRange(from = 1, to = 10) int factor) {
        this.factor = factor;
    }

    @Override
    public void transformPage(View page, float position) {
        View child;
        if (page instanceof ViewGroup && (child = ((ViewGroup) page).getChildAt(0)) != null) {
            int pageWidth = page.getWidth();
            if (position >= -1 && position <= 1) {;
                float translationX = pageWidth * -position / factor;
                if (position <= 0) {
                    child.setTranslationX(translationX);
                    if (position == 0) {
                        page.setTranslationX(translationX);
                    }
                } else {
                    child.setTranslationX(0);
                    page.setTranslationX(translationX);
                }
            } else {
                child.setTranslationX(0);
                page.setTranslationX(0);
            }
        }
    }
}
import android.view.View;

import androidx.viewpager.widget.ViewPager;

/**
 * 描述 ViewPager的翻页动画,这里的代码是从Google官方的文档中拷贝过来的。
 * 创建人 kelin
 * 创建时间 2017/10/17  上午9:30
 * 版本 v 1.0.0
 */

class ZoomOutPageTransformer implements ViewPager.PageTransformer {
    private static final float MIN_SCALE = 0.85f;
    private static final float MIN_ALPHA = 0.5f;

    public void transformPage(View view, float position) {
        int pageWidth = view.getWidth();
        int pageHeight = view.getHeight();

        if (position < -1) { // [-Infinity,-1)
            // This page is way off-screen to the left.
            view.setAlpha(0);

        } else if (position <= 1) { // [-1,1]
            // Modify the default slide transition to shrink the page as well
            float scaleFactor = Math.max(MIN_SCALE, 1 - Math.abs(position));
            float vertMargin = pageHeight * (1 - scaleFactor) / 2;
            float horzMargin = pageWidth * (1 - scaleFactor) / 2;
            if (position < 0) {
                view.setTranslationX(horzMargin - vertMargin / 2);
            } else {
                view.setTranslationX(-horzMargin + vertMargin / 2);
            }

            // Scale the page down (between MIN_SCALE and 1)
            view.setScaleX(scaleFactor);
            view.setScaleY(scaleFactor);

            // Fade the page relative to its size.
            view.setAlpha(MIN_ALPHA +
                    (scaleFactor - MIN_SCALE) /
                            (1 - MIN_SCALE) * (1 - MIN_ALPHA));

        } else { // (1,+Infinity]
            // This page is way off-screen to the right.
            view.setAlpha(0);
        }
    }
}

4.布局   activity_guide

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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">

    <com.hjq.demo.ui.activity.sgf.sgfbanner.banner.view.BannerView
        android:id="@+id/bvGuide"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:bannerIndicator="@+id/pivIndicator"
        app:loopMode="fromCoverToCover" />


    <com.hjq.demo.ui.activity.sgf.sgfbanner.banner.view.PointIndicatorView
        android:id="@+id/pivIndicator"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="6dp"
        android:visibility="gone"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.79"
        app:pointColor="#30000000"
        app:pointRadius="4dp"
        app:selectedPointColor="#EB6F5A"
        tools:totalCount="3" />

    <Button
        android:id="@+id/btnExperienceNow"
        android:layout_width="0dp"
        android:layout_height="45dp"
        android:alpha="0"
        android:background="@drawable/bg_guide_button"
        android:text="@string/experience_now"
        android:textColor="@android:color/white"
        android:textSize="16sp"
        android:visibility="invisible"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@id/pivIndicator"
        app:layout_constraintVertical_bias="0.2"
        app:layout_constraintWidth_percent="0.4"
        tools:alpha="1"
        tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout>

 layout_guide_page

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/white">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <androidx.appcompat.widget.AppCompatImageView
            android:id="@+id/ivImage"
            android:layout_width="0dp"
            android:layout_height="0dp"
            tools:src="@drawable/guide_p1_img"
            app:layout_constraintBottom_toTopOf="@id/ivWords"
            app:layout_constraintDimensionRatio="H, 264:252"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintVertical_bias="0.4"
            app:layout_constraintVertical_chainStyle="packed"
            app:layout_constraintWidth_percent="0.7" />

        <androidx.appcompat.widget.AppCompatImageView
            android:id="@+id/ivWords"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:layout_marginTop="40dp"
            tools:src="@drawable/guide_p1_word"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintDimensionRatio="H, 324:114"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toBottomOf="@id/ivImage"
            app:layout_constraintWidth_percent="0.8" />
    </androidx.constraintlayout.widget.ConstraintLayout>

</RelativeLayout>

5.实体类:

BannerEntry

package com.hjq.demo.ui.activity.sgf.sgfbanner.banner;

import android.view.View;
import android.view.ViewGroup;

import java.util.List;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

/**
 * 描述 Banner中的模型
 */

public interface BannerEntry<VALUE> {

    /**
     * 获取当前页面的布局视图。由于{@link com.hjq.demo.ui.activity.sgf.sgfbanner.banner.view.BannerHelper BannerHelper}对返回的View进行了
     * {@link View#setOnTouchListener(View.OnTouchListener)}监听触摸事件的操作,所以你不能再对返回的View进行此操作了。
     * 否者可能会出现手指在触摸时无法停止轮播的bug。
     * @param parent 当前的布局视图的父节点布局。
     * @return 返回当前页面所要显示的View。
     */
    @NonNull
    View onCreateView(ViewGroup parent);

    /**
     * 获取标题。
     * @return 返回当前条目的标题。
     */
    @Nullable
    CharSequence getTitle();

    /**
     * 获取子标题。
     * @return 返回当前条目的子标题。
     */
    @Nullable
    CharSequence getSubTitle();

    /**
     * 获取当前页面的数据。改方法为辅助方法,是为了方便使用者调用而提供的,Api本身并没有任何调用。如果你不需要该方法可以空实现。
     * @return 返回当前页面的数据。
     */
    @Nullable
    VALUE getValue();

    /**
     * 比较两个模型是否相同。这个方法类似于 {@link #equals(Object)} 方法,但是有可能你的 {@link #equals(Object)} 方法另有用途,
     * 且与此方法的意图不一致,如果一致的话你可以直接在实现中返回 {@link #equals(Object)} 方法的返回值。
     * <p>这个方法中的比较没有必要将所有的字段进行比较,只需要区分这个模型的变更会不会影响UI视图,例如标题、图片等改变了则需要返回false,
     * 假如是一些不需要展示在UI视图上的字段发生了改变而需要展示在UI视图上的字段没有发生改变的话则需要返回true。
     * <p>当然,你也可以直接返回Object的equals方法的实现方式(比较哈希地址),这么做也是没有什么问题的,只是在有些时候会导致没有必要的刷新UI视图。例如你的Banner在点击后是要跳转到Web页面的,当你点击后获取到要跳转的url地址后打开WebView页面。但是要跳转的url地址是不需要展示在UI上的,你过你覆盖了该方法且没有对要跳转的url地址进行比较,那么即使在用户下拉刷新后你的某一页的目标url地址发生了改变也不会重新绘制UI,从而提高性能。
     * <p>该方法在 {@link com.hjq.demo.ui.activity.sgf.sgfbanner.banner.view.BannerView#setEntries(List) BannerView.setEntries(@NonNull List<? extends BannerEntry> items)}
     * 或者 {@link com.hjq.demo.ui.activity.sgf.sgfbanner.banner.view.BannerView#setEntries(List, boolean) BannerView.setEntries(@NonNull List<? extends BannerEntry> items, boolean start)}
     * 方法调用后执行,但也不一定就会执行,只有在不是第一次调用 setEntries 方法且上一次的数据源的长度与下一次数据源的长度相同时才会调用。
     * @param newEntry 要比较的对象。
     * @return 该返回值决定了参数中的对象是否与this中所有需要展示在UI视图上的字段一致,如果一致则返回true,否则返回false。如果返回了false则会在
     *  {@link com.hjq.demo.ui.activity.sgf.sgfbanner.banner.view.BannerView#setEntries(List) BannerView.setEntries(@NonNull List<? extends BannerEntry> items)}
     * 或者 {@link com.hjq.demo.ui.activity.sgf.sgfbanner.banner.view.BannerView#setEntries(List, boolean) BannerView.setEntries(@NonNull List<? extends BannerEntry> items, boolean start)}
     * 执行后进行刷新UI视图,如果setEntries方法参数中的所有对象的该方法都返回了true则setEntries方法无效(在该方法被执行的前提下)。
     *
     * @see com.hjq.demo.ui.activity.sgf.sgfbanner.banner.view.BannerView#setEntries(List) BannerView.setEntries(@NonNull List<? extends BannerEntry> items)
     * @see com.hjq.demo.ui.activity.sgf.sgfbanner.banner.view.BannerView#setEntries(List, boolean) BannerView.setEntries(@NonNull List<? extends BannerEntry> items, boolean start)
     */
    boolean same(BannerEntry newEntry);
}

 SimpleBannerEntry

import java.util.List;

import androidx.annotation.Nullable;

/**
 * <strong>描述: </strong> 简单的Banner页面数据模型。
 *
 * @param <D> 该BannerEntry容器中所要盛放的对象的类型。
 */

public abstract class SimpleBannerEntry<D> implements BannerEntry<D> {

    @Nullable
    private final D d;

    /**
     * BannerEntry构造器。
     *
     * @param d 该BannerEntry容器中所要盛放的对象。
     */
    public SimpleBannerEntry(@Nullable D d) {
        this.d = d;
    }

    /**
     * 获取构造器中的您所传入的Object对象。
     *
     * @return 该类就像是一个容器,用来盛放你Banner中的当个页面的对象,而该方法的返回值就是返回这个容器中所盛放的对象。
     */
    @Override
    @Nullable
    public final D getValue() {
        return d;
    }

    /**
     * 获取标题。
     * @return 返回当前条目的标题。
     */
    @Nullable
    @Override
    public CharSequence getTitle() {
        return null;
    }

    /**
     * 获取子标题。
     * @return 返回当前条目的子标题。
     */
    @Nullable
    @Override
    public CharSequence getSubTitle() {
        return null;
    }

    /**
     * 比较两个模型是否相同。这个方法类似于 {@link #equals(Object)} 方法,但是有可能你的 {@link #equals(Object)} 方法另有用途,
     * 且与此方法的意图不一致,如果一致的话你可以直接在实现中返回 {@link #equals(Object)} 方法的返回值。
     * <p>这个方法中的比较没有必要将所有的字段进行比较,只需要区分这个模型的变更会不会影响UI视图,例如标题、图片等改变了则需要返回false,
     * 假如是一些不需要展示在UI视图上的字段发生了改变而需要展示在UI视图上的字段没有发生改变的话则需要返回true。
     * <p>当然,你也可以直接返回Object的equals方法的实现方式(比较哈希地址),这么做也是没有什么问题的,像这里的默认实现就是返回了equals。
     * 只是在有些时候会导致没有必要的刷新UI视图。例如你的Banner在点击后是要跳转到Web页面的,当你点击后获取到要跳转的url地址后打开WebView页面。但是要跳转的url地址是不需要展示在UI上的,你过你覆盖了该方法且没有对要跳转的url地址进行比较,那么即使在用户下拉刷新后你的某一页的目标url地址发生了改变也不会重新绘制UI,从而提高性能。
     * <p>该方法在 {@link com.hjq.demo.ui.activity.sgf.sgfbanner.banner.view.BannerView#setEntries(List) BannerView.setEntries(@NonNull List<? extends BannerEntry> items)}
     * 或者 {@link com.hjq.demo.ui.activity.sgf.sgfbanner.banner.view.BannerView#setEntries(List, boolean) BannerView.setEntries(@NonNull List<? extends BannerEntry> items, boolean start)}
     * 方法调用后执行,但也不一定就会执行,只有在不是第一次调用 setEntries 方法且上一次的数据源的长度与下一次数据源的长度相同时才会调用。
     *
     * @param newEntry 要比较的对象。
     * @return 该返回值决定了参数中的对象是否与this中所有需要展示在UI视图上的字段一致,如果一致则返回true,否则返回false。如果返回了false则会在
     * {@link com.hjq.demo.ui.activity.sgf.sgfbanner.banner.view.BannerView#setEntries(List) BannerView.setEntries(@NonNull List<? extends BannerEntry> items)}
     * 或者 {@link com.hjq.demo.ui.activity.sgf.sgfbanner.banner.view.BannerView#setEntries(List, boolean) BannerView.setEntries(@NonNull List<? extends BannerEntry> items, boolean start)}
     * 执行后进行刷新UI视图,如果setEntries方法参数中的所有对象的该方法都返回了true则setEntries方法无效(在该方法被执行的前提下)。
     * @see com.hjq.demo.ui.activity.sgf.sgfbanner.banner.view.BannerView#setEntries(List) BannerView.setEntries(@NonNull List<? extends BannerEntry> items)
     * @see com.hjq.demo.ui.activity.sgf.sgfbanner.banner.view.BannerView#setEntries(List, boolean) BannerView.setEntries(@NonNull List<? extends BannerEntry> items, boolean start)
     */
    @Override
    public boolean same(BannerEntry newEntry) {
        return equals(newEntry);
    }
}

6.无限轮播自定义类:

package com.hjq.demo.ui.activity.sgf.sgfbanner.banner.view;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Build;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.TextView;

import com.hjq.demo.R;
import com.hjq.demo.ui.activity.sgf.sgfbanner.banner.BannerEntry;
import com.hjq.demo.ui.activity.sgf.sgfbanner.banner.page.Pageable;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.Size;
import androidx.viewpager.widget.ViewPager;

/**
 * 描述 用来显示轮播图的控件。
 */

public class BannerView extends ViewPager {

    /**
     * singlePageMode-没有(不显示)指示器。
     */
    public static final int SINGLE_MODE_NO_INDICATOR = 0x01;
    /**
     * singlePageMode-不可以也不能翻页。
     */
    public static final int SINGLE_MODE_CAN_NOT_PAGING = SINGLE_MODE_NO_INDICATOR << 1;
    /**
     * multiPageMode-无限循环轮播。
     */
    public static final int MULTI_MODE_INFINITE_LOOP = SINGLE_MODE_CAN_NOT_PAGING << 3;
    /**
     * multiPageMode-从头至尾轮播一次。
     */
    public static final int MULTI_MODE_FROM_COVER_TO_COVER = MULTI_MODE_INFINITE_LOOP << 1;
    /**
     * multiPageMode-从头至尾重复轮播。
     */
    public static final int MULTI_MODE_FROM_COVER_TO_COVER_LOOP = MULTI_MODE_FROM_COVER_TO_COVER << 1;

    @NonNull
    private final BannerHelper mBH;


    public BannerView(Context context) {
        this(context, null);
    }

    public BannerView(Context context, AttributeSet attrs) {
        super(context, attrs);

        TypedArray typedArray;
        if (attrs == null || (typedArray = context.obtainStyledAttributes(attrs, R.styleable.BannerView)) == null) {
            mBH = new BannerHelper(this, MULTI_MODE_INFINITE_LOOP);
        } else {
            int interpolatorId = typedArray.getResourceId(R.styleable.BannerView_pagingInterpolator, NO_ID);
            Interpolator interpolator = null;
            if (interpolatorId != NO_ID) {
                interpolator = AnimationUtils.loadInterpolator(getContext(), interpolatorId);
            }

            mBH = new BannerHelper(this,
                    typedArray.getInt(R.styleable.BannerView_singlePageMode, 0) |
                            typedArray.getInt(R.styleable.BannerView_loopMode, MULTI_MODE_INFINITE_LOOP),
                    interpolator,
                    typedArray.getInt(R.styleable.BannerView_pagingIntervalTime, 0),
                    typedArray.getInt(R.styleable.BannerView_decelerateMultiple, 0),
                    typedArray.getResourceId(R.styleable.BannerView_bannerIndicator, NO_ID),
                    typedArray.getResourceId(R.styleable.BannerView_titleView, NO_ID),
                    typedArray.getResourceId(R.styleable.BannerView_subTitleView, NO_ID),
                    typedArray.getBoolean(R.styleable.BannerView_touchPauseEnable, true));
            typedArray.recycle();
        }
    }

    /**
     * 由于我需要监听BannerView的触摸事件,通过该事件来处理什么时候需要暂停和启动轮播图,所以我禁用了这个方法。其实你也并不需要
     * 对Banner的触摸事件进行监听。
     */
    @Override
    @Deprecated
    public void setOnTouchListener(OnTouchListener l) {
        throw new RuntimeException("This method has been disabled");
    }

    /**
     * 由于我需要监听BannerView的触摸事件,通过该事件来处理什么时候需要暂停和启动轮播图,所以我禁用了这个方法。其实你也并不需要
     * 对Banner的触摸事件进行监听。
     */
    @Override
    @Deprecated
    public void setOnClickListener(@Nullable OnClickListener l) {
        throw new RuntimeException("This method has been disabled");
    }

    /**
     * 该方法进制调用,如果你非要调用将会导致Banner有严重的Bug。
     *
     * @param l {@link OnTouchListener}对象。
     */
    @SuppressLint("ClickableViewAccessibility")
    void listenerOnTouch(OnTouchListener l) {
        super.setOnTouchListener(l);
    }

    @Override
    public void removeView(View view) {
        super.removeView(view);
        //下面的代码是解决View复用导致的view层级关系错乱的问题。重新对view的布局参数进行初始化。
        ViewGroup.LayoutParams lp = view.getLayoutParams();
        if (lp != null) {
            try {
                Field positionField = BannerHelper.getField(ViewPager.LayoutParams.class, "position");
                if (positionField != null) {
                    positionField.setInt(lp, 0);
                }
                Field widthFactorField = BannerHelper.getField(ViewPager.LayoutParams.class, "widthFactor");
                if (widthFactorField != null) {
                    widthFactorField.setFloat(lp, 0.f);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 设置条目数据并开始轮播。如果不希望启动轮播则调用两个参数的方法{@link #setEntries(List, boolean)}。
     *
     * @param items {@link BannerEntry} 集合。
     * @see #setEntries(List, boolean)
     */
    public void setEntries(@NonNull List<? extends BannerEntry> items) {
        setEntries(items, true);
    }

    /**
     * 设置条目数据。
     *
     * @param items {@link BannerEntry} 集合。
     * @param start 是否开始轮播。
     */
    public void setEntries(@NonNull List<? extends BannerEntry> items, boolean start) {
        mBH.setEntries(items, start);
    }

    /**
     * 获取数据源集合。
     *
     * @return 返回上一次调用 {@link #setEntries(List)} 或 {@link #setEntries(List, boolean)} 方法成功时的参数。
     */
    public List<? extends BannerEntry> getEntries() {
        return mBH.getEntries();
    }

    /**
     * 设置翻页的间隔时间,单位:毫秒。
     *
     * @param pagingIntervalTime 要设置的时长。
     */
    public void setPagingIntervalTime(@Size(min = 1000) int pagingIntervalTime) {
        mBH.setPagingIntervalTime(pagingIntervalTime);
    }

    /**
     * 设置翻页动画减速倍数。
     *
     * @param multiple 要减速的倍数。默认为ViewPage的6倍。
     */
    public void setDecelerateMultiple(@Size(min = 2) int multiple) {
        mBH.setMultiple(multiple);
    }

    /**
     * 设置点击事件监听。
     *
     * @param listener Banner页面的点击事件监听对象。
     */
    public void setOnPageClickListener(OnPageClickListener listener) {
        mBH.setOnPageClickListener(listener);
    }

    /**
     * 设置页面长按的监听。
     *
     * @param listener Banner页面的长按事件监听对象。
     */
    public void setOnPageLongClickListener(OnPageLongClickListener listener) {
        mBH.setOnPageLongClickListener(listener);
    }

    /**
     * 设置页面改变监听。
     *
     * @param listener Banner页面改变的监听对象。
     */
    public void setOnPageChangedListener(OnPageChangeListener listener) {
        mBH.setOnPageChangedListener(listener);
    }

    /**
     * 如果你想监听页面的改变,应当使用 {@link #setOnPageChangedListener(OnPageChangeListener)} 方法,
     * 因为 {@link OnPageChangeListener} 的回调方法中会把页面中的数据模型回调给你。
     *
     * @param listener {@link ViewPager.OnPageChangeListener}的子类对象。
     * @see #setOnPageChangedListener(OnPageChangeListener)
     */
    @Deprecated
    @Override
    public void addOnPageChangeListener(ViewPager.OnPageChangeListener listener) {
        super.addOnPageChangeListener(listener);
    }

    void addPageChangeListener(ViewPager.OnPageChangeListener listener) {
        super.addOnPageChangeListener(listener);
    }

    /**
     * 设置当Banner只有一张图片时的处理模式。该方法并不推荐使用,建议在XML中通过app:singlePageMode自定义属性配置。
     *
     * @param singlePageMode 要设置的处理模式,可以是{@link #SINGLE_MODE_NO_INDICATOR} 或者是 {@link #SINGLE_MODE_CAN_NOT_PAGING}。
     *                       也可是同时设置两个参数,同时设置两个参数是中间用"|"符号链接。
     *                       例如:"bannerView.setSinglePageMode(BannerView.SINGLE_MODE_NO_INDICATOR|BannerView.SINGLE_MODE_CAN_NOT_PAGING)"。
     *                       如果同时设置了两个参数则表示如果只有一张图片则既不会轮播而且无论你是否设置了指示器则都不会显示。
     * @see #SINGLE_MODE_NO_INDICATOR
     * @see #SINGLE_MODE_CAN_NOT_PAGING
     */
    public void setSinglePageMode(int singlePageMode) {
        mBH.setSinglePageMode(singlePageMode);
    }

    /**
     * 设置页面指示器控件。
     *
     * @param indicatorView {@link Pageable} 对象。
     */
    public <V extends View & Pageable> void setIndicatorView(@NonNull V indicatorView) {
        mBH.setIndicatorView(indicatorView);
    }

    /**
     * 设置标题显示控件。
     *
     * @param titleView 用来显示标题的TextView。
     */
    public void setTitleView(TextView titleView) {
        mBH.setTitleView(titleView);
    }

    /**
     * 设置副标题显示控件。
     *
     * @param subTitleView 用来显示副标题的TextView。
     */
    public void setSubTitleView(TextView subTitleView) {
        mBH.setSubTitleView(subTitleView);
    }

    /**
     * 开始轮播。
     */
    public void start() {
        mBH.start();
    }

    /**
     * 停止轮播。调用此方法可以轮播图从正在轮播的状态改变到停止轮播的状态。该方法一般
     * 情况下你是不需要调用的,假如你是配合可滚动的控件(如ScrollView、ListView或则RecyclerView)使用时希望通过调用
     * 该方法在轮播图不可见时停止轮播的话,那就不必了。因为我已经做好了这件事情。
     */
    public void stop() {
        mBH.stop();
    }

    /**
     * 是否已经启动轮播。
     *
     * @return 如果已经启动播返回true,否则返回false。
     */
    public boolean isStarted() {
        return mBH.isStarted();
    }

    /**
     * 选择中间页,如果你想移动到中间则需要调用这个方法。
     *
     * <br/><br/><br/><strong>必须在{@link #setEntries(List)}或{@link #setEntries(List, boolean)}方法之后调用。</strong>
     */
    public void selectCenterPage() {
        selectCenterPage(0);
    }

    /**
     * 选择中间页,如果你想移动到中间则需要调用这个方法。
     *
     * @param offset 向右偏移的页数。
     *
     * <br/><br/><br/><strong>必须在{@link #setEntries(List)}或{@link #setEntries(List, boolean)}方法之后调用。</strong>
     */
    public void selectCenterPage(int offset) {
        mBH.selectCenterPage(offset);
    }

    /**
     * 设置显示左右两边的页面,调用该方法前你必须在你的布局文件中为 {@link BannerView} 包裹一层布局。而这个布局的触摸事件默认
     * 会传递给 {@link BannerView}。
     */
    public void setShowLeftAndRightPage() {
        setShowLeftAndRightPage(0);
    }

    /**
     * 设置显示左右两边的页面,调用该方法前你必须在你的布局文件中为 {@link BannerView} 包裹一层布局。而这个布局的触摸事件默认
     * 会传递给 {@link BannerView}。
     *
     * @param showWidthDp 两边页面的宽度。单位dp。
     */
    public void setShowLeftAndRightPage(int showWidthDp) {
        setShowLeftAndRightPage(showWidthDp, true, null);
    }

    /**
     * 设置显示左右两边的页面,调用该方法前你必须在你的布局文件中为 {@link BannerView} 包裹一层布局。而这个布局的触摸事件默认
     * 会传递给 {@link BannerView}。
     *
     * @param reverseDrawingOrder 是否翻转动画。
     * @param pageTransformer     {@link ViewPager.PageTransformer} 对象。
     * @see BannerView#setPageTransformer(boolean, ViewPager.PageTransformer)
     */
    public void setShowLeftAndRightPage(boolean reverseDrawingOrder, ViewPager.PageTransformer pageTransformer) {
        setShowLeftAndRightPage(0, reverseDrawingOrder, pageTransformer);
    }

    /**
     * 设置显示左右两边的页面,调用该方法前你必须在你的布局文件中为 {@link BannerView} 包裹一层布局。而这个布局的触摸事件默认
     * 会传递给 {@link BannerView}。
     *
     * @param showWidthDp         两边页面的宽度。单位dp。
     * @param reverseDrawingOrder 是否翻转动画。
     * @param pageTransformer     {@link ViewPager.PageTransformer} 对象。
     * @see ViewPager#setPageTransformer(boolean, ViewPager.PageTransformer)
     */
    public void setShowLeftAndRightPage(int showWidthDp, boolean reverseDrawingOrder, ViewPager.PageTransformer pageTransformer) {
        mBH.setShowLeftAndRightPage(showWidthDp, reverseDrawingOrder, pageTransformer);
    }

    @Override
    public void setPageTransformer(boolean reverseDrawingOrder, PageTransformer transformer, int pageLayerType) {
        super.setPageTransformer(reverseDrawingOrder, transformer, pageLayerType);
        if (Build.VERSION.SDK_INT >= 11) {
            mBH.updatePageTransformer(transformer);
        }
    }

    int determineTargetPage(int currentPage, float pageOffset) {
        try {
            Field lastMotionX = BannerHelper.getField(ViewPager.class, "mLastMotionX");
            Field initialMotionX = BannerHelper.getField(ViewPager.class, "mInitialMotionX");
            int deltaX = (int) (lastMotionX.getFloat(this) - initialMotionX.getFloat(this));
            Method method = ViewPager.class.getDeclaredMethod("determineTargetPage", int.class, float.class, int.class, int.class);
            method.setAccessible(true);
            return (int) method.invoke(this, currentPage, pageOffset, 0, deltaX);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    protected void onWindowVisibilityChanged(int visibility) {
        super.onWindowVisibilityChanged(visibility);
        mBH.onWindowVisibilityChanged(visibility);
    }

    boolean isFirstLayout() {
        Boolean isFirstLayout = true;
        try {
            Field mFirstLayout = BannerHelper.getField(ViewPager.class, "mFirstLayout");
            isFirstLayout = mFirstLayout.getBoolean(this);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return isFirstLayout;
    }

    /**
     * 轮播图的所有事件的监听类。
     */
    public interface OnPageClickListener {

        /**
         * 页面被点击的时候执行。
         *
         * @param entry 当前页面的 {@link BannerEntry} 对象。
         * @param index 当前页面的索引。这个索引永远会在你的集合的size范围内。
         */
        void onPageClick(BannerEntry entry, int index);
    }

    /**
     * 页面长按的监听。
     */
    public interface OnPageLongClickListener {
        /**
         * 页面被长按的时候执行。
         *
         * @param entry 当前页面的 {@link BannerEntry} 对象。
         * @param index 当前页面的索引。这个索引永远会在你的集合的size范围内。
         */
        void onPageLongClick(BannerEntry entry, int index);
    }

    /**
     * 页面改变的监听。
     */
    public interface OnPageChangeListener {


        /**
         * 当页面被选中的时候调用。
         *
         * @param entry 当前页面的 {@link BannerEntry} 对象。
         * @param index 当前页面的索引。这个索引永远会在你的集合的size范围内。
         */
        void onPageSelected(BannerEntry entry, int index);

        /**
         * 当页面正在滚动中的时候执行。
         *
         * @param index                当前页面的索引。这个索引永远会在你的集合的size范围内。
         * @param positionOffset       值为(0,1)表示页面位置的偏移。
         * @param positionOffsetPixels 页面偏移的像素值。
         */
        void onPageScrolled(int index, float positionOffset, int positionOffsetPixels);

        /**
         * 当Banner中的页面的滚动状态改变的时候被执行。
         *
         * @param state 当前的滚动状态。
         * @see BannerView#SCROLL_STATE_IDLE
         * @see BannerView#SCROLL_STATE_DRAGGING
         * @see BannerView#SCROLL_STATE_SETTLING
         */
        void onPageScrollStateChanged(int state);
    }

    /**
     * 页面改变的监听的实现类,这里将所有的方法都进行了空实现。如果你希望监听页面改变而又不想监听所有事件,可以使用该实现类。
     */
    public abstract class OnPageChangeListenerImpl implements OnPageChangeListener {

        /**
         * 当页面被选中的时候调用。
         *
         * @param entry 当前页面的 {@link BannerEntry} 对象。
         * @param index 当前页面的索引。这个索引永远会在你的集合的size范围内。
         */
        @Override
        public void onPageSelected(BannerEntry entry, int index) {
        }

        /**
         * 当页面正在滚动中的时候执行。
         *
         * @param index                当前页面的索引。这个索引永远会在你的集合的size范围内。
         * @param positionOffset       值为(0,1)表示页面位置的偏移。
         * @param positionOffsetPixels 页面偏移的像素值。
         */
        @Override
        public void onPageScrolled(int index, float positionOffset, int positionOffsetPixels) {
        }

        /**
         * 当Banner中的页面的滚动状态改变的时候被执行。
         *
         * @param state 当前的滚动状态。
         * @see BannerView#SCROLL_STATE_IDLE
         * @see BannerView#SCROLL_STATE_DRAGGING
         * @see BannerView#SCROLL_STATE_SETTLING
         */
        @Override
        public void onPageScrollStateChanged(int state) {
        }
    }
}

7.attr

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="BannerView">
        <!--翻页间隔时长-->
        <attr name="pagingIntervalTime" format="integer"/>
        <!--翻页动画减速倍数-->
        <attr name="decelerateMultiple" format="integer"/>
        <!--指示器控件的ViewId-->
        <attr name="bannerIndicator" format="reference"/>
        <!--标题控件-->
        <attr name="titleView" format="reference"/>
        <!--子标题控件-->
        <attr name="subTitleView" format="reference"/>
        <!--翻页动画差值器-->
        <attr name="pagingInterpolator" format="reference"/>
        <!--只有一张图片时的显示模式-->
        <attr name="singlePageMode" format="integer">
            <!--没有(不显示)指示器-->
            <flag name="noIndicator" value="0x01"/>
            <!--不可以也不能翻页-->
            <flag name="canNotPaging" value="0x02"/>
        </attr>
        <!--拥有多张图片时的显示模式-->
        <attr name="loopMode" format="enum">
            <!--无限循环模式-->
            <enum name="infiniteLoop" value="0x10" />
            <!--从头至尾循环一次-->
            <enum name="fromCoverToCover" value="0x20" />
            <!--从头至尾一直循环-->
            <enum name="fromCoverToCoverLoop" value="0x40" />
        </attr>
        <!--触摸暂停轮播是否可用-->
        <attr name="touchPauseEnable" format="boolean" />
    </declare-styleable>

    <declare-styleable name="BannerIndicator">
        <!--总页数,如果是配合BannerView使用的则以BannerView的页数为准。这个属性最大的用途就是在写布局文件时可以看到效果。-->
        <attr name="totalCount" format="integer"/>
        <attr name="android:gravity"/>
    </declare-styleable>

    <declare-styleable name="PointIndicatorView">
        <!--点的半径-->
        <attr name="pointRadius" format="dimension"/>
        <!--选中的点的半径-->
        <attr name="selectedPointRadius" format="dimension"/>
        <!--点与点之间的间距,默认为最小的点的直径。-->
        <attr name="pointSpacing" format="dimension"/>
        <!--点的颜色-->
        <attr name="pointColor" format="color"/>
        <!--选中时点的颜色-->
        <attr name="selectedPointColor" format="color"/>
    </declare-styleable>
    <declare-styleable name="PointIndicator2View">
        <!--点的半径-->
        <attr name="pointRadius1" format="dimension"/>
        <!--选中的点的半径-->
        <attr name="selectedPointRadius1" format="dimension"/>
        <!--点与点之间的间距,默认为最小的点的直径。-->
        <attr name="pointSpacing1" format="dimension"/>
        <!--&lt;!&ndash;点的颜色&ndash;&gt;-->
        <!--<attr name="pointColor" format="color"/>-->
        <!--&lt;!&ndash;选中时点的颜色&ndash;&gt;-->
        <!--<attr name="selectedPointColor" format="color"/>-->
    </declare-styleable>

    <declare-styleable name="NumberIndicatorView">
        <!--字体大小-->
        <attr name="android:textSize"/>
        <!--所有的字体颜色-->
        <attr name="android:textColor"/>
        <!--分割符号文本-->
        <attr name="separator" format="string"/>
        <!--分隔符文本颜色-->
        <attr name="separatorTextColor" format="color"/>
        <!--当前页面码字体颜色-->
        <attr name="currentPageTextColor" format="color"/>
        <!--总页数字体颜色-->
        <attr name="totalPageTextColor" format="color"/>
    </declare-styleable>
</resources>

相关源码:

https://github.com/zhpanvip/BannerViewPager/tree/master

https://github.com/kelinZhou/Banner

二.效果二

当然这里需要nineoldandroids-2.4.0.jar这个jar包的帮助,下载地址:http://download.csdn.net/my

1.------------------------------MainActivity.java-----------------------------

 

import java.util.ArrayList;

import com.example.day9viewpager1.rotate.RotateDownTransformer;

import android.app.Activity;
import android.os.Bundle;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.PageTransformer;
import android.view.View;

public class MainActivity extends Activity {

	private ViewPager vp;
	private ArrayList<Integer> list;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		vp = (ViewPager) findViewById(R.id.vp);
		
		list = new ArrayList<Integer>();
		list.add(R.drawable.ty1);
		list.add(R.drawable.ty2);
		list.add(R.drawable.ty3);
		list.add(R.drawable.ty4);
		list.add(R.drawable.ty5);
		
		vp.setAdapter(new MyPager(MainActivity.this, list));
		//记得把相应的包发进来=nineoldandroids-2.4.0.jar包
		vp.setPageTransformer(true, new RotateDownTransformer());
	}
	
}

 

 

 

 

 

2.------------------------------pager.xml-----------------------------

 

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:src="@drawable/ic_launcher" />

</LinearLayout>

 

 

 

 

 

3.----------------------------activity_main.xml-------------------------------

 

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
     >

     <android.support.v4.view.ViewPager
        android:id="@+id/vp"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</RelativeLayout>

 

 

 

 

 

4.--------------------------MyPager.java---------------------------------

 

import java.util.ArrayList;

import android.content.Context;
import android.support.v4.view.PagerAdapter;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;

public class MyPager extends PagerAdapter {

	private Context context;
	private ArrayList<Integer> list;

	public MyPager(Context context,ArrayList<Integer> list) {
		// TODO Auto-generated constructor stub
		this.context=context;
		this.list=list;
	}
	@Override
	public int getCount() {
		// TODO Auto-generated method stub
		return list.size();
	}

	@Override
	public boolean isViewFromObject(View arg0, Object arg1) {
		// TODO Auto-generated method stub
		return arg0==arg1;
	}
	@Override
	public Object instantiateItem(ViewGroup container, int position) {
		// TODO Auto-generated method stub
		View view = View.inflate(context, R.layout.pager, null);
		ImageView imagView = (ImageView) view.findViewById(R.id.imageView);
		imagView.setImageResource(list.get(position));
		container.addView(view);
		return view;
	}
	@Override
	public void destroyItem(ViewGroup container, int position, Object object) {
		// TODO Auto-generated method stub
		container.removeView((View) object);
	}

}

 

 

 

 

 

5.--------------------旋转的类---------RotateDownTransformer.java------------------------------

 

import android.support.v4.view.ViewPager.PageTransformer;
import android.util.Log;
import android.view.View;

import com.nineoldandroids.view.ViewHelper;

/**
 * ViewPager自定义旋转动画
 * 
 */
public class RotateDownTransformer implements PageTransformer {

	// 旋转的最大角度为20度
	private static final float MAX_ROTATE = 20.0f;
	// 旋转过程中的角度
	private float currentRotate;

	@Override
	public void transformPage(View view, float position) {
		int pageWidth = view.getWidth();
//		Log.i("TAG", "view = " + view + ",position = " + position);
		if (position < -1) {
			ViewHelper.setRotation(view, 0);
		} else if (position <= 0) {
			// position范围[-1.0,0.0],此时A页动画移出屏幕
			currentRotate = position * MAX_ROTATE;
			// 设置当前页的旋转中心点,横坐标是屏幕宽度的1/2,纵坐标为屏幕的高度
			ViewHelper.setPivotX(view, pageWidth / 2);
			ViewHelper.setPivotY(view, view.getHeight());
			ViewHelper.setRotation(view, currentRotate);
		} else if (position <= 1) {
			// position范围(0.0,1.0],此时B页动画移到屏幕
			currentRotate = position * MAX_ROTATE;
			// 设置当前页的旋转中心点,横坐标是屏幕宽度的1/2,纵坐标为屏幕的高度
			ViewHelper.setPivotX(view, pageWidth / 2);
			ViewHelper.setPivotY(view, view.getHeight());
			ViewHelper.setRotation(view, currentRotate);
		} else {
			ViewHelper.setRotation(view, 0);
		}
	}
}

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值