这是我参与11月更文挑战的第25天,活动详情查看:2021最后一次更文挑战
在文章打造Android一体式轮播广告条中,我们已经学习了打造Android一体式轮播广告条的制作,在上篇文章中我们已经通过一种方法实现了广告条的无线循环。主要做的目的就是让ViewPager和指示器进行封装,方便我们使用,不需要多次进行指示器的控制。看过最后的效果图我们发现上篇的原理中,从最后一张图切换到第一张的时候,效果显得很突兀。所以我们尝试使用第二种思路进行。
第二种思路:通过给PageCount设置一个很大的数,来回实现效果。我们先来看看我们实现的原理:
根据上图,就是利用切换的循环倒置。让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();
}
});
}
}
这样就算完成了,看下效果图:
打造一体式广告轮播条升级版
在博客打造Android一体式轮播广告条和打造Android一体化轮播广告条中,我们已经实现了滚动广告条的滚动,滚动的原理和处理方式都是差不多的,现在我们来看一种新的方式:
通过连续两篇文章的分析,自由无限滚动已经不是重点了,我们的重点是如何对显示的指示器进行布局,通过效果图我们发现,我们需要在底部用一个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();
}
}
以上就是相比较上两篇文章的不同之处,他们的滚动原理完全可以抽取出来,通过继承,减少代码量。看下效果图: