打造一体式广告轮播条升级版

186 阅读4分钟

这是我参与11月更文挑战的第25天,活动详情查看:2021最后一次更文挑战

在文章打造Android一体式轮播广告条中,我们已经学习了打造Android一体式轮播广告条的制作,在上篇文章中我们已经通过一种方法实现了广告条的无线循环。主要做的目的就是让ViewPager和指示器进行封装,方便我们使用,不需要多次进行指示器的控制。看过最后的效果图我们发现上篇的原理中,从最后一张图切换到第一张的时候,效果显得很突兀。所以我们尝试使用第二种思路进行。

第二种思路:通过给PageCount设置一个很大的数,来回实现效果。我们先来看看我们实现的原理:

yuanli

根据上图,就是利用切换的循环倒置。让ViewPager走不到尽头,所以就实现了一种“假”循环的现象。本次的开发还是以上篇博客中的代码为基础进行开发,只在实现原理的位置进行了修改。

在这次的原理中,我们新增了两个成员变量:

	/**
	 * 设定的总循环个数
	 */
	private int pageCount;
	/**
	 * 设定的总页面数
	 */
	private int numberPages;

这两个用于记录我们的页面信息,通常我们会设定一个很大的数作为总循环个数,但是注意,总循环数要是总页面的整数倍,不然循环判断的逻辑就要修改,所以此处,我也不指定,我直接赋值为总页面数的二倍。见代码:

	/**
	 * 设置图片的地址,从网络加载图片
	 * @param imageUrls
	 */
	public void addImageUrls(List<Bitmap> imageUrls) {
		this.imageUrls.addAll(imageUrls);
		numberPages = this.imageUrls.size();
		pageCount = 2 * numberPages;
		createImageView(imageUrls);
	}

看过上篇文章的朋友会发现,我们上篇文章中是传入url地址,直接在内部进行加载图片,这次由于在内部循环,会导致ViewPager内部缓存的ImageView与需要添加的重复,导致报错(一个ImageView已经属于一个Container,需要进行删除)。所以为了方便,我将参数改为了Bitmap,然后在内部进行生成ImageView。

	@Override
	public Object instantiateItem(ViewGroup container, int position) {
		position %= numberPages;
		ImageView imageView = null;
		imageView = new ImageView(context);
		imageView.setScaleType(ScaleType.FIT_XY);
		imageView.setBackgroundDrawable(new BitmapDrawable(imageUrls.get(position)));
		imageView.setTag(position);
		imageView.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View view) {
				if(viewPagerClick != null){
					viewPagerClick.viewPagerOnClick(view);
				}
			}
		});

		container.addView(imageView);
		return imageView;
	}

这次的切换逻辑不是在onPageSelected中进行处理,而是在finishUpdate中进行切换。

	@Override
	public void finishUpdate(ViewGroup container) {
		if(numberPages == 1)return;
		int location = viewPager.getCurrentItem();
		if(location == pageCount - 1){//为最后一张的时候,转换到顶部
			location = numberPages -1;
			viewPager.setCurrentItem(location,false);
		}else if(location == 0){//为第一张的时候,切换到中间
			location = numberPages;
			viewPager.setCurrentItem(location,false);
		}
	}

其次一点就是通过Handler发送消息实现循环的处理。

	private Handler mHandler = new Handler(){

		@Override
		public void handleMessage(Message msg) {
		   int currentPosition = viewPager.getCurrentItem();
		   currentPosition = (currentPosition + 1) % pageCount;
	       if (currentPosition == pageCount - 1) {
	    	   viewPager.setCurrentItem(numberPages - 1, false);
	       } else {
	    	   viewPager.setCurrentItem(currentPosition);
	       }
		   mHandler.sendEmptyMessageDelayed(0, 3000);
		}
	};

至此,基本的逻辑都已经实现完成。具体的代码就不贴了。相比较上篇介绍的,我们这次又新增了几个自定义属性,

   <attr name="indicatorBigSize" format="dimension"/>		<!-- 指示器选中时大小 -->
	<attr name="indicatorBackgroud" format="reference"/>	<!-- 指示器背景色 -->
    <attr name="indicatorMarginBottom" format="dimension"/>	<!-- 指示器距离底部 -->

我们介绍一个使用实例,看下布局文件:

	<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
	    xmlns:tools="http://schemas.android.com/tools"
	    xmlns:viewpager="http://schemas.android.com/apk/res/com.dsw.viewindicator"
	    android:layout_width="match_parent"
	    android:layout_height="match_parent"
	    android:orientation="vertical"
	    tools:context="com.lcwang.androidviews.MainActivity" >
	
		<com.lcwang.androidviews.ViewPagerBarnner
		    android:id="@+id/viewPager"
		    android:layout_height="160dp"
		    android:layout_width="match_parent"
		    viewpager:indicatorSize="8dp"
		    viewpager:indicatorInterval="10dp"
		    viewpager:containerHeight="15dp"
		    viewpager:indicatorMarginBottom="0dp"
		    viewpager:indicatorBackgroud="@drawable/head_sbox"
		    viewpager:indicatorDrawable="@drawable/indicator_backgroud"
		    />
		
		<com.lcwang.androidviews.first.ViewPagerBarnner
		    android:id="@+id/viewPagerFirst"
		    android:layout_height="160dp"
		    android:layout_width="match_parent"
		    android:layout_marginTop="20dp"
		    viewpager:indicatorSize="8dp"
		    viewpager:indicatorInterval="10dp"
		    viewpager:containerHeight="15dp"
		    viewpager:indicatorBackgroud="@drawable/head_sbox"
		    viewpager:indicatorMarginBottom="0dp"
		    viewpager:indicatorDrawable="@drawable/indicator_backgroud"
		    />
	</LinearLayout>

MainActivity中的逻辑:

	public class MainActivity extends Activity {
		private List<Bitmap> url;
		private List<String> urlString;
		private ViewPagerBarnner viewPagerBarnner;
		private com.lcwang.androidviews.first.ViewPagerBarnner viewPagerBarnner1;
		private LinearLayout linear_layout;
		@Override
		protected void onCreate(Bundle savedInstanceState) {
			super.onCreate(savedInstanceState);
			setContentView(R.layout.activity_main);
			viewPagerBarnner = (ViewPagerBarnner) findViewById(R.id.viewPager);
			viewPagerBarnner1 = (com.lcwang.androidviews.first.ViewPagerBarnner) findViewById(R.id.viewPagerFirst);
			url = new ArrayList<Bitmap>();
			url.add(BitmapFactory.decodeResource(getResources(), R.drawable.first));
			url.add(BitmapFactory.decodeResource(getResources(), R.drawable.second));
			url.add(BitmapFactory.decodeResource(getResources(), R.drawable.third));
			url.add(BitmapFactory.decodeResource(getResources(), R.drawable.four));
			url.add(BitmapFactory.decodeResource(getResources(), R.drawable.five));
			url.add(BitmapFactory.decodeResource(getResources(), R.drawable.six));
			
			viewPagerBarnner.addImageUrls(url);
			viewPagerBarnner.setViewPagerClick(new ViewPagerClick() {
				
				@Override
				public void viewPagerOnClick(View view) {
					Toast.makeText(MainActivity.this, "click:" + view.getTag().toString(), Toast.LENGTH_SHORT).show();
				}
			});
			
			urlString = new ArrayList<String>();
			urlString.add("drawable://" + R.drawable.first);
			urlString.add("drawable://" + R.drawable.second);
			urlString.add("drawable://" + R.drawable.third);
			urlString.add("drawable://" + R.drawable.four);
			urlString.add("drawable://" + R.drawable.five);
			urlString.add("drawable://" + R.drawable.six);
			viewPagerBarnner1.addImageUrls(urlString);
			viewPagerBarnner1.setViewPagerClick(new com.lcwang.androidviews.first.ViewPagerBarnner.ViewPagerClick() {
				
				@Override
				public void viewPagerOnClick(View view) {
					Toast.makeText(MainActivity.this, "click:" + view.getTag().toString(), Toast.LENGTH_SHORT).show();
				}
			});
		}
	}

这样就算完成了,看下效果图:

result

打造一体式广告轮播条升级版

在博客打造Android一体式轮播广告条打造Android一体化轮播广告条中,我们已经实现了滚动广告条的滚动,滚动的原理和处理方式都是差不多的,现在我们来看一种新的方式:

flashbannar

通过连续两篇文章的分析,自由无限滚动已经不是重点了,我们的重点是如何对显示的指示器进行布局,通过效果图我们发现,我们需要在底部用一个RelativeLayout进行布局。我们直接看代码:

	/**
 * 初始化View的视图
 */
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);
	
	//底部的RelativeLayout
	relative_Bottom = new RelativeLayout(context);
	LayoutParams layoutParams = new LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, (int)containerHeight);
	layoutParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
	relative_Bottom.setLayoutParams(layoutParams);
	relative_Bottom.setPadding(5, 5, 5, 5);
	if(indicatorBackgroud != 0){
		relative_Bottom.setBackgroundDrawable(context.getResources().getDrawable(indicatorBackgroud));
	}
	
	//左边的文字指示
	tv_pageName = new TextView(context);
	layoutParams = new LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
	layoutParams.addRule(RelativeLayout.CENTER_VERTICAL);
	layoutParams.leftMargin = 20;
	tv_pageName.setLayoutParams(layoutParams);
	tv_pageName.setTextColor(Color.parseColor("#000000"));
	tv_pageName.setTextSize(16);
	//指示器容器
	indicatorView = new LinearLayout(context);
	indicatorView.setOrientation(LinearLayout.HORIZONTAL);
	layoutParams = new LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
	layoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
	layoutParams.addRule(RelativeLayout.CENTER_VERTICAL);
	layoutParams.bottomMargin = (int) indicatorMarginBottom;
	indicatorView.setGravity(Gravity.CENTER_VERTICAL | Gravity.CENTER_HORIZONTAL);
	indicatorView.setLayoutParams(layoutParams);
	
	relative_Bottom.addView(tv_pageName);
	relative_Bottom.addView(indicatorView);
	addView(viewPager);
	addView(relative_Bottom);
	mHandler.sendEmptyMessageDelayed(0, 3000);
}

在上面的代码中,我们在底部放置一个RelativeLayout布局,然后在它的左边放置了一个TextView,在右边放置了一个LinearLayout布局用于存储我们的指示器。布局好了,就开始我们的指示器开发,在效果图中,指示器是TextView,我们只需要添加对应的TextView即可。

	/** 
 * 从url地址创建imageview对象,同时初始化指示器
 */
private void createImageView(List<Bitmap> imageUrlList){
	if(imageUrlList != null && imageUrlList.size() > 0){
		TextView pointView;
		int position = 0;
		while(position < imageUrlList.size()){
			pointView = new TextView(context);
			LinearLayout.LayoutParams params = new LinearLayout.LayoutParams((int)(indicatorSize),(int)(indicatorSize));
			params.rightMargin = (int)indicatorInterval;
			pointView.setLayoutParams(params);
			pointView.setBackgroundDrawable(context.getResources().getDrawable(indicatorDrawable));
			pointView.setEnabled(false);
			pointView.setText((position+1)+"");
			pointView.setGravity(Gravity.CENTER_HORIZONTAL | Gravity.CENTER_VERTICAL);
			pointView.setTextSize(12);
			pointView.setTextColor(Color.parseColor("#454545"));
			indicatorView.addView(pointView);
			position++;
		}

		viewPagerAdapter.notifyDataSetChanged();
	}
}

以上就是相比较上两篇文章的不同之处,他们的滚动原理完全可以抽取出来,通过继承,减少代码量。看下效果图: 这里写图片描述