对应轮播图的效果在app中随处可见,实现的方式也比较多,也有很多第三方好用的开源库,比如banner轮播图,当然了,如果不想使用第三方或者第三方在某种程度上不能满足开发需要时,自己实现也并不是太复杂,多半会采用Viewpager、Handler等方式来实现轮播图的效果,这里并没有采用该种方式,而是采用自定义ViewGroup、Handler、Timer等方式来实现的。
在自定义ViewGroup时需要注意容器的宽度测量,应该根据子view测量的宽度*子view的总数;
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//获取子view的数量
children = getChildCount();
if (0 == children) {
setMeasuredDimension(0, 0);
} else {
//测量子视图的宽度和高度
measureChildren(widthMeasureSpec, heightMeasureSpec);
View view = getChildAt(0);
childWidth = view.getMeasuredWidth();
childHeight = view.getMeasuredHeight();
//所有子视图宽度的总和
int width = view.getMeasuredWidth() * children;
setMeasuredDimension(width, childHeight);
}
}
测量出录播图的大小后,就需要在onLayout对显示轮播图的ImageView进行摆放,在摆放时对于top就是0,bottom就是录播图显示的高度,需要动态改变的就是它显示的left和right,第一个ImageView的left就是0,后面的话就是上一个ImageView的right,right就是left和轮播图宽度的和;
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
if (changed) {
int leftMargin = 0;
for (int i = 0; i < children; i++) {
View view = getChildAt(i);
view.layout(leftMargin, 0, leftMargin + childWidth, childHeight);
leftMargin += childWidth;
}
}
}
测量和摆放好后,通过scrollTo方法和Scroller类中的startScroll方法进行滑动,在刚创建完毕需要computeScroll方法中将它移动到第一个显示;
@Override
public void computeScroll() {
super.computeScroll();
if (scroller.computeScrollOffset()) {
scrollTo(scroller.getCurrX(), 0);
invalidate();
}
}
然后在onInterceptTouchEvent和onTouchEvent方法中处理手势;
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return true;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
//按下后停止轮播
stopAuto();
if (!scroller.isFinished()) {
//如果没有完成 结束滑动过程
scroller.abortAnimation();
}
isClick = true;
x = event.getX();
break;
case MotionEvent.ACTION_MOVE:
float moveX = event.getX();
float distance = moveX - x;
if (Math.abs(distance) > 10) {
isClick = false;
}
scrollBy((int) -distance, 0);
x = moveX;
break;
case MotionEvent.ACTION_UP:
int scrollX = getScrollX();
index = (scrollX + childWidth / 2) / childWidth;
if (index < 0) {//已经滑动到最左边那张图片
index = 0;
} else if (index > children - 1) {
index = children - 1;//滑动到最右边的图片
}
if (isClick) {
//点击事件
if (listener != null) {
listener.clickImageIndex(index);
}
} else {
}
//计算滑动的距离
int dx = index * childWidth - scrollX;
// scrollTo(index * childWidth, 0);
scroller.startScroll(scrollX, 0, dx, 0, 500);
postInvalidate();
//通知指示器改变
if (pointListener != null) {
pointListener.selectImage(index);
}
//当用户松开时又重新开启图片轮播
startAuto();
break;
}
return true;
}
轮播图的点击和指示器的改变都是在onTouchEvent方法中,然后通过接口回调的方式来实现的,接下来就是自动轮播的实现了,采用Handler、Timer、TimerTask的方式来实现的自动轮播,实例化一个TimerTask,每隔一段时间由Timer来执行,然后在Handler中去处理轮播;
/**
* 初始化话Scroller TimerTask 实现自动轮播
*/
private void initObj() {
scroller = new Scroller(getContext());
task = new TimerTask() {
@Override
public void run() {
if (isAuto) {
//开启轮播图
autoHandler.sendEmptyMessage(0);
}
}
};
timer.schedule(task, 100, 2500);
}
private Handler autoHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 0:
int duration = 1000;
//实现图片的自动轮播
if (++index >= children) {
//最后一张图片 从第一张图片开始重新滑动
index = 0;
duration = 1;
}
int scrollX = getScrollX();
int dx = index * childWidth - scrollX;
scroller.startScroll(scrollX, 0, dx, 0, duration);
postInvalidate();
//通知指示器改变
if (pointListener != null) {
pointListener.selectImage(index);
}
break;
}
}
};
在定义ViewGroup中并没有具体的轮播图显示ImageView的创建,而是在另外一个自定容器中来实现,将轮播图和指示器放在另外一个自定义容器中,在其构造方法中就对之前写好的轮播图和指示器容器进行初始化;
/**
* 加载图片轮播
*/
private void initImageBannerViewGroup() {
imageBarnnerViewGroup = new ImageBarnnerViewGroup(getContext());
FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
imageBarnnerViewGroup.setLayoutParams(lp);
addView(imageBarnnerViewGroup);
//设置轮播图切换监听
imageBarnnerViewGroup.setPointSelectListener(new ImageBarnnerViewGroup.ImageBannerViewGroupListener() {
@Override
public void selectImage(int position) {
int count = linearLayout.getChildCount();
for (int i = 0; i < count; i++) {
ImageView childAt = (ImageView) linearLayout.getChildAt(i);
if (position == i) {
childAt.setImageResource(R.drawable.dot_select);
} else {
childAt.setImageResource(R.drawable.dot_normal);
}
}
}
});
//设置点击轮播图点击事件监听
imageBarnnerViewGroup.setOnImageBannerListener(new ImageBarnnerViewGroup.ImageBannerListener() {
@Override
public void clickImageIndex(int position) {
if (listener != null) {
listener.clickImageIndex(position);
}
}
});
}
/**
* 加载底部指示器
*/
private void initDotLinearLayout() {
linearLayout = new LinearLayout(getContext());
FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, dip2px(40));
linearLayout.setLayoutParams(lp);
linearLayout.setOrientation(LinearLayout.HORIZONTAL);
linearLayout.setGravity(Gravity.CENTER);
addView(linearLayout);
FrameLayout.LayoutParams layoutParams = (LayoutParams) linearLayout.getLayoutParams();
layoutParams.gravity = Gravity.BOTTOM;
linearLayout.setLayoutParams(layoutParams);
}
在使用时根据调用addPoint方法传入的参数进行ImageView和指示器view的创建;
/**
* 调用该方法设置轮播图的数量
* @param list 传入轮播的数据源
*/
public void addPoint(List<Integer> list) {
for (Integer integer : list) {
addBitmapToImageBannerViewGroup(integer);
addDotToLinearLayout();
}
}
/**
* 添加轮播的指示器
*/
private void addDotToLinearLayout() {
ImageView imageView = new ImageView(getContext());
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
lp.rightMargin = dip2px(5);
lp.leftMargin = dip2px(5);
imageView.setLayoutParams(lp);
imageView.setImageResource(R.drawable.dot_normal);
linearLayout.addView(imageView);
}
/**
* 添加轮播的图片
* @param resId
*/
private void addBitmapToImageBannerViewGroup(int resId) {
ImageView iv = new ImageView(getContext());
iv.setScaleType(ImageView.ScaleType.CENTER_CROP);
ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(screenWidth, ViewGroup.LayoutParams.MATCH_PARENT);
iv.setImageResource(resId);
iv.setLayoutParams(lp);
imageBarnnerViewGroup.addView(iv);
}
具体代码的实现都是在自定义ViewGroup和自定义容器中,外部通过addPoint、startAuto、stopAuto、destoryAuto等方法进行轮播图的创建、开启自动轮播、暂停自动轮播、销毁轮播图等操作;
public class MainActivity extends AppCompatActivity {
private List<Integer> ids;
private ImageBannerFramLayout bannerLayout;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bannerLayout = findViewById(R.id.banner_layout);
ids = new ArrayList<>();
ids.add(R.mipmap.banner);
ids.add(R.mipmap.baner01);
ids.add(R.mipmap.banner02);
ids.add(R.mipmap.baner04);
ids.add(R.mipmap.banner05);
ids.add(R.mipmap.banner06);
bannerLayout.addPoint(ids);
bannerLayout.setOnImageBannerListener(new ImageBannerFramLayout.ImageBannerListener() {
@Override
public void clickImageIndex(int position) {
Toast.makeText(MainActivity.this, "点击了" + position, Toast.LENGTH_LONG).show();
}
});
}
@Override
protected void onStop() {
super.onStop();
bannerLayout.stopAuto();
}
@Override
protected void onRestart() {
super.onRestart();
bannerLayout.startAuto();
}
@Override
protected void onDestroy() {
super.onDestroy();
bannerLayout.destoryAuto();
}
}
完整代码:
/**
* 自定义轮播图效果
*/
public class ImageBarnnerViewGroup extends ViewGroup {
//获取子view的数量
private int children;
//子视图宽度
private int childWidth;
//子视图高度
private int childHeight;
//第一次按下的x位置 每一次移动过程中 移动之前的位置左边
private float x;
//每张图片的索引
private int index = 0;
private Scroller scroller;
//是否自动轮播 默认情况下开启自动轮播
private boolean isAuto = true;
private Timer timer = new Timer();
private TimerTask task;
private Handler autoHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 0:
int duration = 1000;
//实现图片的自动轮播
if (++index >= children) {
//最后一张图片 从第一张图片开始重新滑动
index = 0;
duration = 1;
}
int scrollX = getScrollX();
int dx = index * childWidth - scrollX;
scroller.startScroll(scrollX, 0, dx, 0, duration);
postInvalidate();
//通知指示器改变
if (pointListener != null) {
pointListener.selectImage(index);
}
break;
}
}
};
private ImageBannerListener listener;
private ImageBannerViewGroupListener pointListener;
//是否是点击事件
private boolean isClick = false;
/**
* 开始播放轮播图
*/
public void startAuto() {
isAuto = true;
}
/**
* 暂停播放轮播图
*/
public void stopAuto() {
isAuto = false;
}
/**
* 当页面销毁的时候调用该方法
*/
public void destoryAuto() {
isAuto = false;
if (autoHandler != null) {
//移除handler消息
autoHandler.removeCallbacksAndMessages(null);
}
if (task != null) {
task.cancel();
task = null;
}
if (timer != null) {
timer.cancel();
timer = null;
}
}
public ImageBarnnerViewGroup(Context context) {
this(context, null);
}
public ImageBarnnerViewGroup(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ImageBarnnerViewGroup(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initObj();
}
/**
* 初始化话Scroller TimerTask 实现自动轮播
*/
private void initObj() {
scroller = new Scroller(getContext());
task = new TimerTask() {
@Override
public void run() {
if (isAuto) {
//开启轮播图
autoHandler.sendEmptyMessage(0);
}
}
};
timer.schedule(task, 100, 2500);
}
@Override
public void computeScroll() {
super.computeScroll();
if (scroller.computeScrollOffset()) {
scrollTo(scroller.getCurrX(), 0);
invalidate();
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//获取子view的数量
children = getChildCount();
if (0 == children) {
setMeasuredDimension(0, 0);
} else {
//测量子视图的宽度和高度
measureChildren(widthMeasureSpec, heightMeasureSpec);
View view = getChildAt(0);
childWidth = view.getMeasuredWidth();
childHeight = view.getMeasuredHeight();
//所有子视图宽度的总和
int width = view.getMeasuredWidth() * children;
setMeasuredDimension(width, childHeight);
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
if (changed) {
int leftMargin = 0;
for (int i = 0; i < children; i++) {
View view = getChildAt(i);
view.layout(leftMargin, 0, leftMargin + childWidth, childHeight);
leftMargin += childWidth;
}
}
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return true;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
//按下后停止轮播
stopAuto();
if (!scroller.isFinished()) {
//如果没有完成 结束滑动过程
scroller.abortAnimation();
}
isClick = true;
x = event.getX();
break;
case MotionEvent.ACTION_MOVE:
float moveX = event.getX();
float distance = moveX - x;
if (Math.abs(distance) > 10) {
isClick = false;
}
scrollBy((int) -distance, 0);
x = moveX;
break;
case MotionEvent.ACTION_UP:
int scrollX = getScrollX();
index = (scrollX + childWidth / 2) / childWidth;
if (index < 0) {//已经滑动到最左边那张图片
index = 0;
} else if (index > children - 1) {
index = children - 1;//滑动到最右边的图片
}
if (isClick) {
//点击事件
if (listener != null) {
listener.clickImageIndex(index);
}
} else {
}
//计算滑动的距离
int dx = index * childWidth - scrollX;
// scrollTo(index * childWidth, 0);
scroller.startScroll(scrollX, 0, dx, 0, 500);
postInvalidate();
//通知指示器改变
if (pointListener != null) {
pointListener.selectImage(index);
}
//当用户松开时又重新开启图片轮播
startAuto();
break;
}
return true;
}
/**
* 设置轮播图点击事件监听
*
* @param listener
*/
public void setOnImageBannerListener(ImageBannerListener listener) {
this.listener = listener;
}
/**
* 轮播图点击接口
*/
public interface ImageBannerListener {
void clickImageIndex(int position);
}
/**
* 轮播图滑动监听
*/
public interface ImageBannerViewGroupListener {
void selectImage(int position);
}
/**
* 设置轮播图滑动监听
*
* @param listener
*/
public void setPointSelectListener(ImageBannerViewGroupListener listener) {
this.pointListener = listener;
}
}
/**
* 自定义轮播图
*/
public class ImageBannerFramLayout extends FrameLayout {
//图片轮播
private ImageBarnnerViewGroup imageBarnnerViewGroup;
//屏幕的宽度
private int screenWidth;
//底部指示器容器
private LinearLayout linearLayout;
//点击监听回调接口
private ImageBannerListener listener;
public ImageBannerFramLayout(Context context) {
this(context, null);
}
public ImageBannerFramLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ImageBannerFramLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
screenWidth = getScreenWidth();
initImageBannerViewGroup();
initDotLinearLayout();
}
/**
* 加载图片轮播
*/
private void initImageBannerViewGroup() {
imageBarnnerViewGroup = new ImageBarnnerViewGroup(getContext());
FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
imageBarnnerViewGroup.setLayoutParams(lp);
addView(imageBarnnerViewGroup);
//设置轮播图切换监听
imageBarnnerViewGroup.setPointSelectListener(new ImageBarnnerViewGroup.ImageBannerViewGroupListener() {
@Override
public void selectImage(int position) {
int count = linearLayout.getChildCount();
for (int i = 0; i < count; i++) {
ImageView childAt = (ImageView) linearLayout.getChildAt(i);
if (position == i) {
childAt.setImageResource(R.drawable.dot_select);
} else {
childAt.setImageResource(R.drawable.dot_normal);
}
}
}
});
//设置点击轮播图点击事件监听
imageBarnnerViewGroup.setOnImageBannerListener(new ImageBarnnerViewGroup.ImageBannerListener() {
@Override
public void clickImageIndex(int position) {
if (listener != null) {
listener.clickImageIndex(position);
}
}
});
}
/**
* 加载底部指示器
*/
private void initDotLinearLayout() {
linearLayout = new LinearLayout(getContext());
FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, dip2px(40));
linearLayout.setLayoutParams(lp);
linearLayout.setOrientation(LinearLayout.HORIZONTAL);
linearLayout.setGravity(Gravity.CENTER);
addView(linearLayout);
FrameLayout.LayoutParams layoutParams = (LayoutParams) linearLayout.getLayoutParams();
layoutParams.gravity = Gravity.BOTTOM;
linearLayout.setLayoutParams(layoutParams);
}
/**
* 调用该方法设置轮播图的数量
* @param list 传入轮播的数据源
*/
public void addPoint(List<Integer> list) {
for (Integer integer : list) {
addBitmapToImageBannerViewGroup(integer);
addDotToLinearLayout();
}
}
/**
* 添加轮播的指示器
*/
private void addDotToLinearLayout() {
ImageView imageView = new ImageView(getContext());
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
lp.rightMargin = dip2px(5);
lp.leftMargin = dip2px(5);
imageView.setLayoutParams(lp);
imageView.setImageResource(R.drawable.dot_normal);
linearLayout.addView(imageView);
}
/**
* 添加轮播的图片
* @param resId
*/
private void addBitmapToImageBannerViewGroup(int resId) {
ImageView iv = new ImageView(getContext());
iv.setScaleType(ImageView.ScaleType.CENTER_CROP);
ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(screenWidth, ViewGroup.LayoutParams.MATCH_PARENT);
iv.setImageResource(resId);
iv.setLayoutParams(lp);
imageBarnnerViewGroup.addView(iv);
}
private int dip2px(int dip) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, getResources().getDisplayMetrics());
}
/**
* 获取手机屏幕的宽度
*
* @return
*/
private int getScreenWidth() {
WindowManager windowManager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics displayMetrics = new DisplayMetrics();
windowManager.getDefaultDisplay().getMetrics(displayMetrics);
return displayMetrics.widthPixels;
}
public void setOnImageBannerListener(ImageBannerListener listener) {
this.listener = listener;
}
public interface ImageBannerListener {
void clickImageIndex(int position);
}
/**
* 开始播放轮播图
*/
public void startAuto(){
imageBarnnerViewGroup.startAuto();
}
/**
* 暂停播放轮播图
*/
public void stopAuto(){
imageBarnnerViewGroup.stopAuto();
}
/**
* 当页面销毁的时候调用该方法
*/
public void destoryAuto(){
imageBarnnerViewGroup.destoryAuto();
}
}
网友评论