广告栏初始样貌
我们来逐步实现广告栏,首先我们做的只是带点点的ViewPager,只能手动滑动,并且文字对应变化
直接看GIF效果和代码
注意一下,point_selector.xml、point_normal.xml、point_press.xml需要放在drawable下面:
ViewPagerAvtivity:
package com.ly.updating;
import android.os.Bundle;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import java.util.ArrayList;
/**
* Created by ly on 2016/12/20.
*/
public class ViewPagerActivity extends AppCompatActivity {
private ViewPager viewpager;
private TextView tv_title;//文字textView
private LinearLayout ll_point_group;//点点布局
// 因为大家都了解ListView,先说一下ListView的使用,然后我们对比着,看一下ViewPager的使用
// ListView的使用
// 1、在布局文件中定义ListView
// 2、在代码中实例化ListView
// 3、准备数据
// 4、设置适配器(BaseAdapter)-item布局-绑定数据
// ArrayList不安全但是效率高
// 这里是用于准备数据,这个就是图片数据的集合
private ArrayList<ImageView> imageViews;
// 图片资源ID集合
private final int[] imageIds = {R.drawable.a, R.drawable.b, R.drawable.c, R.drawable.d, R.drawable.e};
// 图片标题集合
private final String[] imageDescriptions = {"艾克", "菲奥娜", "艾希", "卡特", "亚索"};
int prePosition = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.viewpager);
viewpager = (ViewPager) findViewById(R.id.viewpager);
tv_title = (TextView) findViewById(R.id.tv_title);
ll_point_group = (LinearLayout) findViewById(R.id.ll_point_group);
// ViewPager的使用
// 1、在布局文件中定义ViewPager
// 2、在代码中实例化ViewPager
// 3、准备数据
// 4、设置适配器(PagerAdapter)-item布局-绑定数据
imageViews = new ArrayList<>();
for (int i = 0; i < imageIds.length;i++) {
ImageView imageView = new ImageView(this);
// 设置src会按比例填充,但是设置background会拉伸填充
// 我们要的效果是拉伸填充,所以这里使用setBackgroundResource
imageView.setBackgroundResource(imageIds[i]);
// 添加到集合中
imageViews.add(imageView);
// 添加点点
ImageView point = new ImageView(this);
point.setBackgroundResource(R.drawable.point_selector);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(8,8);
if (i==0){
point.setEnabled(true); // 显示红色
}else {
point.setEnabled(false); // 显示灰色
params.leftMargin = 20;//点点之间的距离(像素)
}
// android:gravity="center" 布局中设置居中,所以这里不用再设置了
point.setLayoutParams(params);
ll_point_group.addView(point);
}
viewpager.setAdapter(new MyPagerAdapter());
viewpager.addOnPageChangeListener(new MyOnPageChangeListener());
tv_title.setText(imageDescriptions[0]);
}
class MyOnPageChangeListener implements ViewPager.OnPageChangeListener{
/**
* 当页面滚动了的时候回调这个方法
* @param position 当前页面的位置
* @param positionOffset 滑动页面的百分比
* @param positionOffsetPixels 在屏幕上滑动的像素
*/
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
/**
* 当某个页面被选中
* @param position 被选中的页面的位置
*/
@Override
public void onPageSelected(int position) {
// 设置对应页面的文本信息
tv_title.setText(imageDescriptions[position]);
// 设置点点的颜色
ll_point_group.getChildAt(prePosition).setEnabled(false); // 上一页页面对应点点设置为灰色
ll_point_group.getChildAt(position).setEnabled(true); // 当前页面对应点点设置为红色
prePosition = position; // 记录当前点点
}
/**
* 当页面滚动 状态的变化 回调这个方法
* 静止 -> 滑动
* 滑动 -> 静止
* 静止 -> 拖拽
* @param state
*/
@Override
public void onPageScrollStateChanged(int state) {
}
}
class MyPagerAdapter extends PagerAdapter{
/**
* @return 图片的总数
*/
@Override
public int getCount() {
return imageViews.size();
}
/**
* 相当于getView方法
* @param container ViewPager自身
* @param position 当前实例化页面的位置
* @return
*/
@Override
public Object instantiateItem(ViewGroup container, int position) {
ImageView imageView = imageViews.get(position);
container.addView(imageView); // 把图片添加到ViewPager容器中
return imageView;
}
/**
* 比较view和object是否是同一个实力
* @param view 页面
* @param object instantiateItem返回的结果
* @return
*/
@Override
public boolean isViewFromObject(View view, Object object) {
if (view == object) {
return true;
}else {
return false;
}
}
/**
* 释放资源
* @param container viewpager
* @param position 要释放的位置
* @param object 要释放的页面
*/
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View) object);
}
}
}
viewpager.xml:
<?xml version="1.0" encoding="utf-8"?>
<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"
tools:context=".ViewPagerActivity">
<android.support.v4.view.ViewPager
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="200dp" />
<LinearLayout
android:id="@+id/linearLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/viewpager"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:background="@drawable/gradient_background"
android:orientation="vertical">
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:padding="2dp"
android:text="name"
android:textColor="#ffffff"
android:textStyle="bold" />
</LinearLayout>
<LinearLayout
android:id="@+id/ll_point_group"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="@+id/linearLayout"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:gravity="center"
android:orientation="horizontal"></LinearLayout>
</RelativeLayout>
point_selector.xml:(变颜色)
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/point_normal" android:state_enabled="false" />
<item android:drawable="@drawable/point_press" android:state_enabled="true" />
</selector>
point_normal.xml:(灰色)
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<size
android:width="8dp"
android:height="8dp" />
<solid android:color="#44000000" />
</shape>
point_press.xml:(红色)
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<size
android:width="8dp"
android:height="8dp" />
<solid android:color="#ff0000" />
</shape>
gradient_background.xml:(渐变背景)随便加的 不会的可以学习一下
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<gradient
android:angle="90"
android:centerColor="#aaaaaa"
android:endColor="#6000"
android:startColor="#FF9900"
android:type="linear"></gradient>
</shape>
如图:
实现无限循环
82行 onCreate
/**
* 设置中间位置 向左向右都可以滑动1073741820次
* 简单的说 这里设置一个很大的上限 让轮播图片看起来好像是无限循环
* 实际没有用户会向左或者向右滑动1073741820次 所以就是无限循环的效果
* 同时,要在MyPagerAdapter和MyOnPageChangeListener中做对应修改
*/
// 保证imageViews的整数倍 Integer.MAX_VALUE=2147483647
int item = Integer.MAX_VALUE/2 - Integer.MAX_VALUE/2%imageViews.size();
//System.out.println(item); // 1073741820
/**
* setCurrentItem
* 设置当前选定的页面。如果ViewPager已经通过它的第一
* 布局与当前适配器将有一个平稳的动画之间的过渡
* 当前项目和指定项目。
*/
viewpager.setCurrentItem(item); // 设置当前选定的页面。即,中间位置。
120行 MyOnPageChangeListener-------onPagerSelected
int realPosition = position%imageViews.size();
// 设置对应页面的文本信息
//tv_title.setText(imageDescriptions[position]);
tv_title.setText(imageDescriptions[realPosition]);
// 设置点点的颜色
ll_point_group.getChildAt(prePosition).setEnabled(false); // 上一页页面对应点点设置为灰色
//ll_point_group.getChildAt(position).setEnabled(true); // 当前页面对应点点设置为红色
ll_point_group.getChildAt(realPosition).setEnabled(true); // 当前页面对应点点设置为红色
//prePosition = position; // 记录当前点点
prePosition = realPosition; // 记录当前点点
153行 MyPagerAdapter------getCount
return Integer.MAX_VALUE;
164行 MyPagerAdapter------instantiateItem
int realPosition = position%imageViews.size();
// ImageView imageView = imageViews.get(position);
ImageView imageView = imageViews.get(realPosition);
container.addView(imageView); // 把图片添加到ViewPager容器中
实现无限循环自动播放
写一个handler,每隔2秒换下一张照片
/**
* 实例化一个Handler 让轮播自动循环
*/
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
int item = viewpager.getCurrentItem()+1;
viewpager.setCurrentItem(item);
// 延迟发消息
handler.sendEmptyMessageDelayed(0,2000);
}
};
onCreate 中发一个空消息,启动这个handler
// 发消息
handler.sendEmptyMessageDelayed(0,500);
滑动触摸增加用户体验
因为用户在按住图片的时候,图片还是会动的,所以为了用户体验,使用户在按的时候保持图片不动,进行修改
全局:
// 拖动是否为真,辅助判断图片触摸事件
private boolean isDragging = false;
161行 MyOnPageChangeListener-------onPageScrollStateChanged
/**
* 当页面滚动 状态的变化 回调这个方法
* 静止 -> 滑动
* 滑动 -> 静止
* 静止 -> 拖拽
* @param state
*/
@Override
public void onPageScrollStateChanged(int state) {
if (state == ViewPager.SCROLL_STATE_DRAGGING){ // 拖动
System.out.println("拖动为真");
isDragging = true; // 拖动为真
}else if (state == ViewPager.SCROLL_STATE_SETTLING){ // 持续(滚动)
}else if (state == ViewPager.SCROLL_STATE_IDLE&&isDragging){ // 闲置(静止)
System.out.println("拖动为假");
isDragging = false; // 拖动为假
handler.removeCallbacksAndMessages(null);
handler.sendEmptyMessageDelayed(0,500);
}
}
194行 MyPagerAdapter--------instantiateItem
/**
* 相当于getView方法
* @param container ViewPager自身
* @param position 当前实例化页面的位置
* @return
*/
@Override
public Object instantiateItem(ViewGroup container, int position) {
int realPosition = position%imageViews.size();
// ImageView imageView = imageViews.get(position);
ImageView imageView = imageViews.get(realPosition);
container.addView(imageView); // 把图片添加到ViewPager容器中
/**
* 添加触摸事件,当图片被触摸,停止自动播放,当触摸停止,继续自动播放
* 时间使用,建议轮播时间时间3-4
* 但是,只在这里判断是不够,当用户开始滑动图片,优先调用ACTION_CANCEL,不会
* 调用到ACTION_MOVE,这里需要详细了解可以Log一下这几个触摸事件看一下,系统是
* 如何接受用户触摸事件的
* 所以还需要在MyOnPageChangeListener的onPageScrollStateChanged
* 的方法里,添加判断
*/
imageView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
switch (motionEvent.getAction()){
case MotionEvent.ACTION_DOWN: // 按下
handler.removeCallbacksAndMessages(null);
break;
case MotionEvent.ACTION_UP: // 抬起
handler.removeCallbacksAndMessages(null);
handler.sendEmptyMessageDelayed(0,500);
break;
case MotionEvent.ACTION_CANCEL: // 取消
break;
case MotionEvent.ACTION_MOVE: // 移动
break;
}
return true;
}
});
return imageView;
}
增加点击事件
-------------------------------------------------------------------
imageView.setOnTouchListener(return false)
return false;// 返回为false事件继续,返回true事件销毁,因为要监听图片点击,所以不能销毁
194行 MyPagerAdapter--------instantiateItem
/**
* 图片点击监听
*/
imageView.setTag(realPosition);
imageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
int realPosition = (int) view.getTag();
String text = imageDescriptions[realPosition];
Toast.makeText(ViewPagerActivity.this, "点击了" + text, Toast.LENGTH_SHORT).show();
}
});
return imageView;
}
两张图bug
我们在测试的时候发现如果是2张图的话,会报错
原因:
一般来说,destroyitem在viewpager移除一个item时调用。 viewpager一般都会缓冲3个item,即一开始就会调用3次instantiateItem,
当向右滑动,到第3页时,第1页的item会被调用到destroyitem。
于是乎:
/**
* 释放资源
* @param container viewpager
* @param position 要释放的位置
* @param object 要释放的页面
*/
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
if(imageViews.size()>2){
container.removeView((View) object);
}
}
这样就可以
总体代码
public class ViewPagerActivity extends AppCompatActivity {
private static final String TAG = "ViewPagerActivity";
private ViewPager viewpager;
private TextView tv_title;//文字textView
private LinearLayout ll_point_group;//点点布局
// 因为大家都了解ListView,先说一下ListView的使用,然后我们对比着,看一下ViewPager的使用
// ListView的使用
// 1、在布局文件中定义ListView
// 2、在代码中实例化ListView
// 3、准备数据
// 4、设置适配器(BaseAdapter)-item布局-绑定数据
// ArrayList不安全但是效率高
// 这里是用于准备数据,这个就是图片数据的集合
private ArrayList<ImageView> imageViews;
// 图片资源ID集合
private final int[] imageIds = {R.drawable.a, R.drawable.b};
// private final int[] imageIds = {R.drawable.a, R.drawable.b, R.drawable.c};
// private final int[] imageIds = {R.drawable.a, R.drawable.b, R.drawable.c, R.drawable.d, R.drawable.e};
// 图片标题集合
private final String[] imageDescriptions = {"艾克", "菲奥娜", "艾希", "卡特", "亚索"};
int prePosition = 0;
// 拖动是否为真,辅助判断图片触摸事件
private boolean isDragging = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.viewpager);
viewpager = (ViewPager) findViewById(R.id.viewpager);
tv_title = (TextView) findViewById(R.id.tv_title);
ll_point_group = (LinearLayout) findViewById(R.id.ll_point_group);
// ViewPager的使用
// 1、在布局文件中定义ViewPager
// 2、在代码中实例化ViewPager
// 3、准备数据
// 4、设置适配器(PagerAdapter)-item布局-绑定数据
imageViews = new ArrayList<>();
for (int i = 0; i < imageIds.length;i++) {
ImageView imageView = new ImageView(this);
// 设置src会按比例填充,但是设置background会拉伸填充
// 我们要的效果是拉伸填充,所以这里使用setBackgroundResource
imageView.setBackgroundResource(imageIds[i]);
// 添加到集合中
imageViews.add(imageView);
// 添加点点
ImageView point = new ImageView(this);
point.setBackgroundResource(R.drawable.point_selector);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(8,8);
if (i==0){
point.setEnabled(true); // 显示红色
}else {
point.setEnabled(false); // 显示灰色
params.leftMargin = 20;//点点之间的距离(像素)
}
// android:gravity="center" 布局中设置居中,所以这里不用再设置了
point.setLayoutParams(params);
ll_point_group.addView(point);
}
viewpager.setAdapter(new MyPagerAdapter());
viewpager.addOnPageChangeListener(new MyOnPageChangeListener());
/**
* 设置中间位置 向左向右都可以滑动1073741820次
* 简单的说 这里设置一个很大的上限 让轮播图片看起来好像是无限循环
* 实际没有用户会向左或者向右滑动1073741820次 所以就是无限循环的效果
* 同时,要在MyPagerAdapter和MyOnPageChangeListener中做对应修改
*/
// 保证imageViews的整数倍 Integer.MAX_VALUE=2147483647
int item = Integer.MAX_VALUE/2 - Integer.MAX_VALUE/2%imageViews.size();
//System.out.println(item); // 1073741820
/**
* setCurrentItem
* 设置当前选定的页面。如果ViewPager已经通过它的第一
* 布局与当前适配器将有一个平稳的动画之间的过渡
* 当前项目和指定项目。
*/
viewpager.setCurrentItem(item); // 设置当前选定的页面。即,中间位置。
tv_title.setText(imageDescriptions[0]);
// 发消息
handler.sendEmptyMessageDelayed(0,500);
}
/**
* 实例化一个Handler 让轮播自动循环
*/
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
int item = viewpager.getCurrentItem()+1;
viewpager.setCurrentItem(item);
// 延迟发消息
handler.sendEmptyMessageDelayed(0,2000);
}
};
class MyOnPageChangeListener implements ViewPager.OnPageChangeListener{
/**
* 当页面滚动了的时候回调这个方法
* @param position 当前页面的位置
* @param positionOffset 滑动页面的百分比
* @param positionOffsetPixels 在屏幕上滑动的像素
*/
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
/**
* 当某个页面被选中
* @param position 被选中的页面的位置
*/
@Override
public void onPageSelected(int position) {
int realPosition = position%imageViews.size();
// 设置对应页面的文本信息
//tv_title.setText(imageDescriptions[position]);
tv_title.setText(imageDescriptions[realPosition]);
// 设置点点的颜色
ll_point_group.getChildAt(prePosition).setEnabled(false); // 上一页页面对应点点设置为灰色
//ll_point_group.getChildAt(position).setEnabled(true); // 当前页面对应点点设置为红色
ll_point_group.getChildAt(realPosition).setEnabled(true); // 当前页面对应点点设置为红色
//prePosition = position; // 记录当前点点
prePosition = realPosition; // 记录当前点点
}
/**
* 当页面滚动 状态的变化 回调这个方法
* 静止 -> 滑动
* 滑动 -> 静止
* 静止 -> 拖拽
* @param state
*/
@Override
public void onPageScrollStateChanged(int state) {
if (state == ViewPager.SCROLL_STATE_DRAGGING){ // 拖动
System.out.println("拖动为真");
isDragging = true; // 拖动为真
}else if (state == ViewPager.SCROLL_STATE_SETTLING){ // 持续(滚动)
}else if (state == ViewPager.SCROLL_STATE_IDLE&&isDragging){ // 闲置(静止)
System.out.println("拖动为假");
isDragging = false; // 拖动为假
handler.removeCallbacksAndMessages(null);
handler.sendEmptyMessageDelayed(0,2000);
}
}
}
class MyPagerAdapter extends PagerAdapter{
int realPosition;
/**
* @return 图片的总数
*/
@Override
public int getCount() {
// return imageViews.size();
return Integer.MAX_VALUE;
}
/**
* 相当于getView方法
* @param container ViewPager自身
* @param position 当前实例化页面的位置
* @return
*/
@Override
public Object instantiateItem(ViewGroup container, int position) {
realPosition = position%imageViews.size();
// ImageView imageView = imageViews.get(position);
ImageView imageView = imageViews.get(realPosition);
ViewGroup parent = (ViewGroup) imageView.getParent();
if (parent != null) {
parent.removeView(imageView);
}
container.addView(imageView);
// container.addView(imageView); // 把图片添加到ViewPager容器中
/**
* 添加触摸事件,当图片被触摸,停止自动播放,当触摸停止,继续自动播放
* 时间使用,建议轮播时间时间3-4
* 但是,只在这里判断是不够,当用户开始滑动图片,优先调用ACTION_CANCEL,不会
* 调用到ACTION_MOVE,这里需要详细了解可以Log一下这几个触摸事件看一下,系统是
* 如何接受用户触摸事件的
* 所以还需要在MyOnPageChangeListener的onPageScrollStateChanged
* 的方法里,添加判断
*/
imageView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
switch (motionEvent.getAction()){
case MotionEvent.ACTION_DOWN: // 按下
handler.removeCallbacksAndMessages(null);
break;
case MotionEvent.ACTION_UP: // 抬起
handler.removeCallbacksAndMessages(null);
handler.sendEmptyMessageDelayed(0,2000);
break;
case MotionEvent.ACTION_CANCEL: // 取消
break;
case MotionEvent.ACTION_MOVE: // 移动
break;
}
return false;// 返回为false事件继续,返回true事件销毁,因为要监听图片点击,所以不能销毁
}
});
/**
* 图片点击监听
*/
imageView.setTag(realPosition);
imageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
int realPosition = (int) view.getTag();
String text = imageDescriptions[realPosition];
Toast.makeText(ViewPagerActivity.this, "点击了" + text, Toast.LENGTH_SHORT).show();
}
});
return imageView;
}
/**
* 比较view和object是否是同一个实力
* @param view 页面
* @param object instantiateItem返回的结果
* @return
*/
@Override
public boolean isViewFromObject(View view, Object object) {
if (view == object) {
return true;
}else {
return false;
}
}
/**
* 释放资源
* @param container viewpager
* @param position 要释放的位置
* @param object 要释放的页面
*/
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
if(imageViews.size()>2){
container.removeView((View) object);
}
}
}
}
作者水平有限 不喜勿喷