本文主要是介绍自定义ViewPage+底部指示器(广告图片轮播),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
自定义ViewPage+底部指示器(广告图片轮播)
有不少项目都用到了广告图片的轮播,通过ViewPage来实现。每次都写一遍总感觉特别麻烦,今天就自己封装一个自带底部指示器的广告图片轮播CustomerViewPage。
整体View是是在一个RelativeLayout中布局,由ViewPage(广告View)+LinearLayout(底部指示器)组成。如下图
底部指示器轮播是通过一个线程来控制,所以实现了Runnable接口。如下代码解析:
CustomerViewPage的成员变量
<span style="font-size:14px;">public class CustomerViewPage extends RelativeLayout implements Runnable {/*** 要显示的ViewPage对象*/private ViewPager viewPager;/*** 放置底部指示物的子视图*/private LinearLayout viewDots;/*** viewDots上的指示物*/private List<ImageView> dots;/*** ViewPage项*/private List<View> views;/*** 当前显示第几张图*/private int position = 0;/*** 可不可以自动轮转(为true当手触摸时不轮转)*/private boolean isContinue = true;/*** 切换时间/ms*/private long changeTime = 1500;/*** 底部指示物之间的间距,默认2dp*/private int dotsSpacing = 2;/*** 底部指示物大小,默认7dp*/private int circleRadio = 10;/*** 轮播图片线程是否存活*/private boolean isAlive = true;/*** 底部指示物在父View(即viewDots视图)的gravity属性,默认在中间* Gravity.RIGHT | Gravity.LEFT | Gravity.CENTER*/private int gravity = Gravity.RIGHT;
}</span>
这里有三个构造函数,以防在代码中动态添加使用:
public CustomerViewPage(Context context) {this(context, null);}public CustomerViewPage(Context context, AttributeSet attrs) {this(context, attrs, 0);}public CustomerViewPage(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);initView();}
初始化ViewPage和底部LinearLayout:
private void initView() {/*** 初始化ViewPage视图*/viewPager = new ViewPager(getContext());LayoutParams viewPagerlp = new LayoutParams(LayoutParams.MATCH_PARENT,LayoutParams.MATCH_PARENT);addView(viewPager, viewPagerlp);/*** 初始化底部指示器视图*/viewDots = new LinearLayout(getContext());LayoutParams viewDotslp = new LayoutParams(LayoutParams.MATCH_PARENT,LayoutParams.WRAP_CONTENT);viewDotslp.addRule(ALIGN_PARENT_BOTTOM);viewDotslp.bottomMargin = dpTopx(5);viewDots.setGravity(gravity);addView(viewDots, viewDotslp);}
在内部有一个CustomerViewPageAdapter内部适配器:
class CustomerViewPageAdapter extends PagerAdapter {@Overridepublic int getCount() {return views == null ? 0 : views.size();}@Overridepublic boolean isViewFromObject(View arg0, Object arg1) {return arg0 == arg1;}@Overridepublic Object instantiateItem(ViewGroup container, int position) {if (views.get(position).getParent() != null) {((ViewGroup) views.get(position).getParent()).removeView(views.get(position));}container.addView(views.get(position));return views.get(position);}@Overridepublic void destroyItem(ViewGroup container, int position, Object object) {container.removeView(views.get(position));}}
这里特别要注意
instantiateItem这个方法。里面一定要写上下面这段代码,如果不写的话,有可能会抛出非法异常操作。如果只是一个Activity中使用不会有这个异常,但是超过两个地方以上使用这个CustomerViewPage的话就回抛出
illegalStateException(非法操作异常),
这是因为第一次将ViewPage中的VIew添加进去的时候,这个View是没有父控件的,但是当第二次添加的时候,该View已经有了一个父控件了,不允许再有其他的父控件,所以如果该View的父控件不为null,就必须先将该View的从父控件中remove掉,再添加进去。否则就回抛出illegalStateException
if (views.get(position).getParent() != null) {((ViewGroup) views.get(position).getParent()).removeView(views.get(position));}
控制底部指示器轮播的线程:
Handler pagerHandler = new Handler() {public void handleMessage(android.os.Message msg) {viewPager.setCurrentItem(msg.what);super.handleMessage(msg);};};@Overridepublic void run() {while (isAlive) {if (isContinue) {pagerHandler.sendEmptyMessage(position);position = (position + 1) % views.size();try {Thread.sleep(changeTime);} catch (InterruptedException e) {}}}}
这个就是暴露在外部的一个接口,设置ViewPage的View:
public void setViewPageViews(List<View> views) {this.views = views;// 初始化底部的圆addDots(views.size());viewPager.setAdapter(new CustomerViewPageAdapter());viewPager.setOnPageChangeListener(new OnPageChangeListener() {@Overridepublic void onPageSelected(int index) {position = index;for (int i = 0; i < dots.size(); i++) {if (position == i) {dots.get(i).setBackgroundResource(R.drawable.dot_focused);} else {dots.get(i).setBackgroundResource(R.drawable.dot_normal);}}}@Overridepublic void onPageScrolled(int arg0, float arg1, int arg2) {}@Overridepublic void onPageScrollStateChanged(int arg0) {}});viewPager.setOnTouchListener(new OnTouchListener() {public boolean onTouch(View v, MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN:case MotionEvent.ACTION_MOVE:isContinue = false;break;case MotionEvent.ACTION_UP:isContinue = true;break;default:isContinue = true;break;}return false;}});new Thread(this).start();}private void addDots(int size) {dots = new ArrayList<ImageView>();for (int i = 0; i < size; i++) {ImageView dot = new ImageView(getContext());LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(dpTopx(circleRadio), dpTopx(circleRadio));params.setMargins(dpTopx(dotsSpacing), 0, dpTopx(dotsSpacing), 0);dot.setLayoutParams(params);if (i == 0) {dot.setBackgroundResource(R.drawable.dot_focused);} else {dot.setBackgroundResource(R.drawable.dot_normal);}dots.add(dot);viewDots.addView(dot);}}
当该页面不可见的时候可以调用stop()方法停止掉该线程,否则她还是会一直跑下去。。。
public void stop() {isAlive = false;}
将dp转化为px:
private int dpTopx(int dp) {float scale = getResources().getDisplayMetrics().density;return (int) (dp * scale + 0.5f);}
基本上就没什么了,代码中都有解释,下面贴上CustomerViewPage整个代码:
package com.tz.viewpagedemo;import java.util.ArrayList;
import java.util.List;
import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Handler;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.OnPageChangeListener;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;/*** * @author TianZhao* Time 2015年11月26日* xiyouMobile*/
@SuppressLint("HandlerLeak")
public class CustomerViewPage extends RelativeLayout implements Runnable {/*** 要显示的ViewPage对象*/private ViewPager viewPager;/*** 放置底部指示物的子视图*/private LinearLayout viewDots;/*** viewDots上的指示物*/private List<ImageView> dots;/*** ViewPage项*/private List<View> views;/*** 当前显示第几张图*/private int position = 0;/*** 可不可以自动轮转(为true当手触摸时不轮转)*/private boolean isContinue = true;/*** 切换时间/ms*/private long changeTime = 1500;/*** 底部指示物之间的间距,默认2dp*/private int dotsSpacing = 2;/*** 底部指示物大小,默认7dp*/private int circleRadio = 10;/*** 轮播图片线程是否存活*/private boolean isAlive = true;/*** 底部指示物在父View(即viewDots视图)的gravity属性,默认在中间* Gravity.RIGHT | Gravity.LEFT | Gravity.CENTER*/private int gravity = Gravity.RIGHT;public CustomerViewPage(Context context) {this(context, null);}public CustomerViewPage(Context context, AttributeSet attrs) {this(context, attrs, 0);}public CustomerViewPage(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);initView();}private void initView() {/*** 初始化ViewPage视图*/viewPager = new ViewPager(getContext());LayoutParams viewPagerlp = new LayoutParams(LayoutParams.MATCH_PARENT,LayoutParams.MATCH_PARENT);addView(viewPager, viewPagerlp);/*** 初始化底部指示器视图*/viewDots = new LinearLayout(getContext());LayoutParams viewDotslp = new LayoutParams(LayoutParams.MATCH_PARENT,LayoutParams.WRAP_CONTENT);viewDotslp.addRule(ALIGN_PARENT_BOTTOM);viewDotslp.bottomMargin = dpTopx(5);viewDots.setGravity(gravity);addView(viewDots, viewDotslp);}public void setViewPageViews(List<View> views) {this.views = views;// 初始化底部的圆addDots(views.size());viewPager.setAdapter(new CustomerViewPageAdapter());viewPager.setOnPageChangeListener(new OnPageChangeListener() {@Overridepublic void onPageSelected(int index) {position = index;for (int i = 0; i < dots.size(); i++) {if (position == i) {dots.get(i).setBackgroundResource(R.drawable.dot_focused);} else {dots.get(i).setBackgroundResource(R.drawable.dot_normal);}}}@Overridepublic void onPageScrolled(int arg0, float arg1, int arg2) {}@Overridepublic void onPageScrollStateChanged(int arg0) {}});viewPager.setOnTouchListener(new OnTouchListener() {public boolean onTouch(View v, MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN:case MotionEvent.ACTION_MOVE:isContinue = false;break;case MotionEvent.ACTION_UP:isContinue = true;break;default:isContinue = true;break;}return false;}});new Thread(this).start();}private void addDots(int size) {dots = new ArrayList<ImageView>();for (int i = 0; i < size; i++) {ImageView dot = new ImageView(getContext());LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(dpTopx(circleRadio), dpTopx(circleRadio));params.setMargins(dpTopx(dotsSpacing), 0, dpTopx(dotsSpacing), 0);dot.setLayoutParams(params);if (i == 0) {dot.setBackgroundResource(R.drawable.dot_focused);} else {dot.setBackgroundResource(R.drawable.dot_normal);}dots.add(dot);viewDots.addView(dot);}}class CustomerViewPageAdapter extends PagerAdapter {@Overridepublic int getCount() {return views == null ? 0 : views.size();}@Overridepublic boolean isViewFromObject(View arg0, Object arg1) {return arg0 == arg1;}@Overridepublic Object instantiateItem(ViewGroup container, int position) {if (views.get(position).getParent() != null) {((ViewGroup) views.get(position).getParent()).removeView(views.get(position));}container.addView(views.get(position));return views.get(position);}@Overridepublic void destroyItem(ViewGroup container, int position, Object object) {container.removeView(views.get(position));}}Handler pagerHandler = new Handler() {public void handleMessage(android.os.Message msg) {viewPager.setCurrentItem(msg.what);super.handleMessage(msg);};};@Overridepublic void run() {while (isAlive) {if (isContinue) {pagerHandler.sendEmptyMessage(position);position = (position + 1) % views.size();try {Thread.sleep(changeTime);} catch (InterruptedException e) {}}}}private int dpTopx(int dp) {float scale = getResources().getDisplayMetrics().density;return (int) (dp * scale + 0.5f);}public void stop() {isAlive = false;}
}
底部的小圆点(指示物)是通过xml加载的:
dot_focused.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"android:shape="oval" ><solid android:color="#aaFFFFFF" /><corners android:radius="10dip" />
</shape>
dot_normal.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"android:shape="oval" ><solid android:color="#33000000" /><corners android:radius="10dip" />
</shape>
接下来就是使用了:
package com.tz.viewpagedemo;import java.util.ArrayList;
import java.util.List;
import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageView;/*** * @author TianZhao* Time 2015年11月26日* xiyouMobile*/
public class MainActivity extends Activity {private CustomerViewPage viewPage;private List<View> views;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);viewPage = (CustomerViewPage) findViewById(R.id.vp);initViews();viewPage.setViewPageViews(views);}private void initViews() {views = new ArrayList<>();ImageView imageView1 = new ImageView(this);ImageView imageView2 = new ImageView(this);ImageView imageView3 = new ImageView(this);ImageView imageView4 = new ImageView(this);ImageView imageView5 = new ImageView(this);imageView1.setBackgroundColor(Color.parseColor("#123456"));views.add(imageView1);imageView2.setBackgroundColor(Color.parseColor("#145826"));views.add(imageView2);imageView3.setBackgroundColor(Color.parseColor("#874592"));views.add(imageView3);imageView4.setBackgroundColor(Color.parseColor("#658415"));views.add(imageView4);imageView5.setBackgroundColor(Color.parseColor("#845163"));views.add(imageView5);}
}
下面是运行效果图:
源码地址:http://download.csdn.net/detail/tianzhaoai/9300821
GitHub源码地址:https://github.com/xyTianZhao/CustomerViewPage
这篇关于自定义ViewPage+底部指示器(广告图片轮播)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!