Creating a Meizu-style Banner with ViewPager
Banner advertising positions are critical in mobile applications due to their revenue generation potential. High-traffic applications can generate substantial daily income from well-designed banners. This guide demonstrates how to implement a banner view similar to Meizu's implementation, which displays portions of adjacent pages during transitions.
Core Implementation Approach
The solution uses ViewPager with custom transformations to achieve the visual effect. The key technique involves manipulating the clipping behavior of the parent contianer and applying scale animations during page transitions.
Layout Configuration for Multi-Page Display
Enable the ViewPager to display multiple pages simultaneously by setting clipChildren="false" in the parent layout and applying margins to the ViewPager:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false"
android:orientation="vertical">
<androidx.viewpager.widget.ViewPager
android:id="@+id/content_pager"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_marginLeft="30dp"
android:layout_marginRight="30dp" />
</LinearLayout>
This configuration allows adjacent pages to remain visible outside the ViewPager's bounds.
Custom Page Transformation Animation
Implement a scale animation using ViewPager.PageTransformer to create the zoom effect during transitions:
public class ScalePageTransformer implements ViewPager.PageTransformer {
private static final float MINIMUM_SCALE = 0.85f;
@Override
public void transformPage(View view, float scrollPosition) {
if (scrollPosition < -1.0f) {
view.setScaleY(MINIMUM_SCALE);
} else if (scrollPosition <= 1.0f) {
float scaleValue = Math.max(MINIMUM_SCALE, 1.0f - Math.abs(scrollPosition));
view.setScaleY(scaleValue);
} else {
view.setScaleY(MINIMUM_SCALE);
}
}
}
Apply the transformer to the ViewPager:
ViewPager pager = findViewById(R.id.content_pager);
pager.setPageTransformer(true, new ScalePageTransformer());
Implementing Infinite Scrolling
Use the large number approach for seamless infinite scrolling by returning Integer.MAX_VALUE from the adapter's getCount() method:
public class InfiniteAdapter extends PagerAdapter {
private List<ImageView> imageResources;
@Override
public int getCount() {
return Integer.MAX_VALUE;
}
private int getActualCount() {
return imageResources.size();
}
private int calculateRealPosition(int adapterPosition) {
return adapterPosition % getActualCount();
}
@Override
public Object instantiateItem(ViewGroup container, int adapterPosition) {
int actualPosition = calculateRealPosition(adapterPosition);
ImageView currentImage = imageResources.get(actualPosition);
container.addView(currentImage);
return currentImage;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((ImageView) object);
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
}
Initialize the ViewPager to start near the middle of the large range:
private int getInitialPosition() {
int startingPoint = Integer.MAX_VALUE / 2;
while (startingPoint % adapter.getActualCount() != 0) {
startingPoint++;
}
return startingPoint;
}
pager.setCurrentItem(getInitialPosition());
Auto-Scrollnig Implementation
Implement automatic scrolling using a Handler:
private final Handler scrollHandler = new Handler();
private final Runnable autoScrollTask = new Runnable() {
@Override
public void run() {
if (isAutoScrollingEnabled) {
int currentIndex = pager.getCurrentItem();
currentIndex++;
pager.setCurrentItem(currentIndex, true);
scrollHandler.postDelayed(this, SCROLL_DELAY);
}
}
};
public void startAutoScroll() {
isAutoScrollingEnabled = true;
scrollHandler.postDelayed(autoScrollTask, SCROLL_DELAY);
}
public void stopAutoScroll() {
isAutoScrollingEnabled = false;
scrollHandler.removeCallbacks(autoScrollTask);
}
Complete Banner Component
Combine these elements into a reusable CustomBannerView component with configurable modes:
public class CustomBannerView extends LinearLayout {
private ViewPager contentPager;
private boolean enableMeizuEffect = true;
private boolean enableInfiniteScroll = true;
public void setDisplayMode(boolean meizuEffect, boolean infiniteScroll) {
this.enableMeizuEffect = meizuEffect;
this.enableInfiniteScroll = infiniteScroll;
applyConfiguration();
}
private void applyConfiguration() {
if (enableMeizuEffect) {
contentPager.setPageTransformer(true, new ScalePageTransformer());
}
if (enableInfiniteScroll) {
setupInfiniteScrolling();
}
}
}
Usage Example
<com.example.CustomBannerView
android:id="@+id/banner_view"
android:layout_width="match_parent"
android:layout_height="200dp"
app:meizu_effect="true"
app:infinite_scroll="true" />
CustomBannerView banner = findViewById(R.id.banner_view);
banner.setPages(imageList, new PageViewHolderCreator() {
@Override
public PageViewHolder createHolder() {
return new ImagePageViewHolder();
}
});
@Override
protected void onResume() {
super.onResume();
banner.startAutoScroll();
}
@Override
protected void onPause() {
super.onPause();
banner.stopAutoScroll();
}