github地址:https://github.com/zerohuan/SlideLayout/tree/master
实际效果图:
该自定义控件继承FrameLayout, 包含一个ViewPager和横向排列的LinearLayout。后者用于包含显示表示轮播位置的点集,使用ViewPager的好处在于可以灵活的定义item的内容,而不仅仅是图片。</p><p></p><p>为了便于使用,通过自定义属性的方式定义了所须的运行参数:</p><p><pre name="code" class="html"><declare-styleable name="SlideLayout">
<attr name="viewpagerId" format="reference"/>
<attr name="dotsId" format="reference"/>
<attr name="autoPlay" format="boolean" />
<attr name="slide_interval" format="integer" />
<attr name="dotRadius" format="dimension" />
<attr name="onDotColor" format="color" />
<attr name="offDotColor" format="color" />
<attr name="strokeColor" format="color" />
</declare-styleable> //轮播容器viewPager
private ViewPager viewPager;
//标示位置的点集
private LinearLayout dots;
//viewPager的资源ID
private int viewPagerId;
//包含点的LinearLayout的资源ID
private int dotsId;
//该手机的px-dp比例倍数
private float scale;
//ViewPager的Adapter
private PagerAdapter adapter;
//是否自动播放
private boolean isAutoPlay;
//播放的间隔
private int interval;
//当前页位置
private int currentItem;
//圆点半径
private float dotRadius;
//是否正在轮播运行
private boolean isRunning;
//位于当前页,点标志的颜色
private int onDotColor;
//未位于当前页,点标志的颜色
private int offDotColor;
//点边框颜色
private int strokeColor;
private final static int SCROLL_WHAT = 0x7549;
使用时,通过Xml文件来定义:
<com.luckymore.ydd.app.view.selfView.SlideLayout
xmlns:attrs="http://schemas.android.com/apk/res-auto"
android:id="@+id/news_slide_bar"
android:layout_width="match_parent"
android:layout_height="match_parent"
swipe:dotsId="@+id/test_1"
attrs:viewpagerId="@+id/test_2"
attrs:autoPlay="true"
attrs:dotRadius="3dp"
attrs:slide_interval="4000"
attrs:offDotColor="@color/alpha_black"
attrs:onDotColor="@color/alpha_white"
attrs:strokeColor="#77626262"
>
<android.support.v4.view.ViewPager android:id="@+id/test_2"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<LinearLayout android:id="@+id/test_1"
android:layout_width="wrap_content"
android:layout_height="20dp"
android:orientation="horizontal"
android:layout_gravity="bottom|center_horizontal"
android:gravity="center"
android:paddingLeft="8dp"
android:paddingRight="8dp"
/>
</com.luckymore.ydd.app.view.selfView.SlideLayout>/**
* 获取自定义参数,在构造器中调用
* @param context
* @param attrs
*/
private void init(Context context, AttributeSet...attrs) {
if(attrs.length > 0) {
TypedArray typedArray = context.obtainStyledAttributes(attrs[0], R.styleable.SlideLayout);
viewPagerId = typedArray.getResourceId(R.styleable.SlideLayout_viewpagerId, -1);
dotsId = typedArray.getResourceId(R.styleable.SlideLayout_dotsId, -1);
scale = getResources().getDisplayMetrics().density;
isAutoPlay = typedArray.getBoolean(R.styleable.SlideLayout_autoPlay, false);
interval = typedArray.getInteger(R.styleable.SlideLayout_slide_interval, 4000);
dotRadius = typedArray.getDimension(R.styleable.SlideLayout_dotRadius, 2f * scale);
onDotColor = typedArray.getColor(R.styleable.SlideLayout_onDotColor, 0x77FFFFFF);
offDotColor = typedArray.getColor(R.styleable.SlideLayout_offDotColor, 0x77000000);
strokeColor = typedArray.getColor(R.styleable.SlideLayout_strokeColor, 0x77626262);
}
}@Override
protected void onFinishInflate() {
super.onFinishInflate();
if(viewPagerId != -1 && dotsId != -1) {
viewPager = (ViewPager)findViewById(viewPagerId);
dots = (LinearLayout)findViewById(dotsId);
viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int i, float v, int i2) {
}
@Override
public void onPageSelected(int i) {
currentItem = i;
for(int j = 0; j < viewPager.getAdapter().getCount(); j++) {
DotView dot = (DotView)dots.getChildAt(j);
if(j == i)
dot.setOn(true);
else
dot.setOn(false);
dot.invalidate();
}
}
@Override
public void onPageScrollStateChanged(int i) {
switch (i) {
case 1:// 手势滑动,空闲中
isAutoPlay = false;
break;
case 2:// 界面切换中
isAutoPlay = true;
break;
case 0:// 滑动结束,即切换完毕或者加载完毕
// 当前为最后一张,此时从右向左滑,则切换到第一张
if (viewPager.getCurrentItem() == viewPager.getAdapter().getCount() - 1 && !isAutoPlay) {
viewPager.setCurrentItem(0);
}
// 当前为第一张,此时从左向右滑,则切换到最后一张
else if (viewPager.getCurrentItem() == 0 && !isAutoPlay) {
viewPager.setCurrentItem(viewPager.getAdapter().getCount() - 1);
}
break;
}
}
});
}
}/**
* 点状UI, 正方形View, 包含一个圆形点
*/
public class DotView extends View {
private Paint mPaint = new Paint();
//两种状态, 是否是当前页
private boolean isOn;
//正方形边长
private int mSize;
public DotView(Context context) {
super(context);
}
public DotView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public DotView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
/**
* 比onDraw先执行
* @param widthMeasureSpec
* @param heightMeasureSpec
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
mSize = (int)Math.ceil(dotRadius) * 3;
setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec));
}
private int measureWidth(int measureSpec) {
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
if (specMode == MeasureSpec.EXACTLY) {
// We were told how big to be
result = specSize;
} else {
// Measure the text
result = mSize + getPaddingLeft() + getPaddingRight();
if (specMode == MeasureSpec.AT_MOST) {
// Respect AT_MOST value if that was what is called for by
// measureSpec
result = Math.min(result, specSize);// 60,480
}
}
return result;
}
private int measureHeight(int measureSpec) {
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
if (specMode == MeasureSpec.EXACTLY) {
// We were told how big to be
result = specSize;
} else {
// Measure the text (beware: ascent is a negative number)
result = mSize + getPaddingTop() + getPaddingBottom();
if (specMode == MeasureSpec.AT_MOST) {
// Respect AT_MOST value if that was what is called for by
// measureSpec
result = Math.min(result, specSize);
}
}
return result;
}
//绘制点
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPaint.setAntiAlias(true);
if(isOn()) {
mPaint.setColor(onDotColor);
} else {
mPaint.setColor(offDotColor);
}
mPaint.setStyle(Paint.Style.FILL);
canvas.drawCircle(dotRadius, dotRadius, dotRadius, mPaint);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(0.7f * scale);
mPaint.setColor(strokeColor);
canvas.drawCircle(dotRadius, dotRadius, dotRadius, mPaint);
}
public boolean isOn() {
return isOn;
}
public void setOn(boolean isOn) {
this.isOn = isOn;
}
}
在SlideLayout的成员函数resetDots填充DotView:
/**
* 重新生成点集UI
*/
public void resetDots() {
int dotCount = viewPager.getAdapter().getCount();
int oldDotCount = dots.getChildCount();
for(int i = 0; i < dotCount; i ++) {
DotView dot;
if(i >= oldDotCount) {
dot = new DotView(getContext());
dots.addView(dot);
} else {
dot = (DotView)dots.getChildAt(i);
}
if(i == viewPager.getCurrentItem()) {
dot.setOn(true);
} else {
dot.setOn(false);
}
}
}
在UI线程中使用SlideLayout主要调用如下两个方法:
/**
* 在UI线程中调用,注入ViewPager的adapter
* @param adapter
*/
public void setViewPagerAdapter(PagerAdapter adapter) {
this.adapter = adapter;
viewPager.setAdapter(adapter);
updateUI();
}
/**
* 在UI线程中调用,修改adapter中数据后, 更新UI
*/
public synchronized void updateUI() {
adapter.notifyDataSetChanged();
resetDots();
if(isAutoPlay)
startAutoScroll();
}
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case SCROLL_WHAT:
synchronized(SlideLayout.this) {
viewPager.setCurrentItem(currentItem);
sendScrollMessage(interval);
}
break;
}
}
};
/**
* 开始自动轮播
*/
public void startAutoScroll() {
if(!isRunning) {
isRunning = true;
sendScrollMessage(interval);
}
}
/**
* 滚动到下一页
* @param delayTimeInMills
*/
private synchronized void sendScrollMessage(long delayTimeInMills) {
/** remove messages before, keeps one message is running at most **/
handler.removeMessages(SCROLL_WHAT);
currentItem = (currentItem + 1) % adapter.getCount();
//通过该方法实现定时轮播
handler.sendEmptyMessageDelayed(SCROLL_WHAT, delayTimeInMills);
}
newsBar.setViewPagerAdapter(new NewsBarAdapter(ret.getNews(), getMactivity()));
原文地址:http://blog.csdn.net/zerohuan/article/details/45742947