循环轮播图

循环轮播图,想必大家都在很多app山看到过,但是具体怎么实现呢?今天小弟就跟大家一种通过组合自定义view来实现的方法。

先看一张效果图:


再看一下结构



现在开始正式讲解如何实现这个轮播图的自定义,首先定义一下轮播图的布局文件:shuffling_picture.xml;

<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="com.example.lunbo.MainActivity" >


    <android.support.v4.view.ViewPager
        android:id="@+id/shuffling_viewpage"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_centerHorizontal="true" />


    <LinearLayout
        android:id="@+id/shuffling_point_layout"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="5dip"
        android:orientation="horizontal" >
    </LinearLayout>


</RelativeLayout>

主要由一个ViewPage和一个LinearLagyout组成,其中ViewPage是用来放要轮播的图片,LineartLayout是用来动态的仿下面的小点,多少张图片就对应多少个点。

接下来看看自定义view的自定义属性文件:attrs.xml;

<?xml version="1.0" encoding="utf-8"?>
<resources>  
     <declare-styleable name="ShufflingPicture">  
        <attr name="pointDrawbleNormal" format="reference"/>  
        <attr name="pointDrawbleSelect" format="reference"/>  
    </declare-styleable>   
</resources>

在这里我主要自定义了两个属性,一个是用来放小点的正常状态时的图片,一个是用来放小点的选择状态时的图片,可能有人会说为什么不用selector来放图片?起初我是用selector来的,但是出现了一些图片显示不出来的问题,所以我就暂时用了这种。

接下来我们再来看看最主要的部分,自定义的轮播图:ShufflingPicture;

package com.demo.shufflingpicture;


import java.util.LinkedList;


import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
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.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;


/**
 * 轮播图
 * 
 * @author fuxingkai
 * 
 */
public class ShufflingPicture extends RelativeLayout implements
OnPageChangeListener, OnTouchListener {


private Context context;
private ViewPager imageViewPager;
private AdapterCycle pagerAdapter;
private LinearLayout pointLinearLayout;
private LinkedList<View> views;
private ImageView[] points;
private Drawable pointDrawableNoraml;
private Drawable pointDrawableSelect;
private boolean isPlay = true;
private boolean left = false;
private boolean right = false;
private boolean isScrolling = false;
private int currentIndex = 0;
private int pointIndex = 1;
private int lastValue = -1;
private ChangeViewCallback changeViewCallback = null;


public ShufflingPicture(Context context) {
super(context);
this.context = context;
initView();
}


public ShufflingPicture(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
initView();
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.ShufflingPicture);
pointDrawableNoraml = a
.getDrawable(R.styleable.ShufflingPicture_pointDrawbleNormal);
pointDrawableSelect = a
.getDrawable(R.styleable.ShufflingPicture_pointDrawbleSelect);
if (pointDrawableNoraml == null) {
pointDrawableNoraml = getResources().getDrawable(
R.drawable.point_normal);
}
;
if (pointDrawableSelect == null) {
pointDrawableSelect = getResources().getDrawable(
R.drawable.point_select);
}
a.recycle();
}


private void initView() {
LayoutInflater.from(context).inflate(R.layout.shuffling_picture, this);
imageViewPager = (ViewPager) findViewById(R.id.shuffling_viewpage);
pointLinearLayout = (LinearLayout) findViewById(R.id.shuffling_point_layout);


}


private void setViewPageAdapter(AdapterCycle pagerAdapter) {
this.pagerAdapter = pagerAdapter;
imageViewPager.setOnPageChangeListener(this);
imageViewPager.setAdapter(pagerAdapter);
imageViewPager.setOffscreenPageLimit(views.size());
imageViewPager.setCurrentItem(1);
imageViewPager.setOnTouchListener(this);
startShuffling();
};


public void setViews(LinkedList<View> listViews) {
views = listViews;
initPoints();
setViewPageAdapter(new AdapterCycle(context, views));
}


// 实现ViewPager.OnPageChangeListener接口
@Override
public void onPageSelected(int position) {
if (pagerAdapter.getCount() > 1) { // 多于1,才会循环跳转
if (position < 1) { // 首位之前,跳转到末尾(N)
position = views.size(); // 注意这里是mList,而不是mViews
imageViewPager.setCurrentItem(position, false);
} else if (position > views.size()) { // 末位之后,跳转到首位(1)
imageViewPager.setCurrentItem(1, false); // false:不显示跳转过程的动画
position = 1;
} else {
setCurDot(position - 1);
}
}


}


private Handler handler = new Handler();
private Runnable runnable = new Runnable() {
@Override
public void run() {
if (isPlay) {
if (pointIndex < views.size() + 1) {
pointIndex++;
} else {
pointIndex = 0;
}
imageViewPager.setCurrentItem(pointIndex);
if (pointIndex < 1) { // 首位之前,跳转到末尾(N)
pointIndex = views.size(); // 注意这里是mList,而不是mViews
imageViewPager.setCurrentItem(pointIndex, true);
} else if (pointIndex > views.size()) { // 末位之后,跳转到首位(1)
imageViewPager.setCurrentItem(1, false); // false:不显示跳转过程的动画
pointIndex = 1;
} else {
setCurDot(pointIndex - 1);
}
}
handler.postDelayed(this, 2000);
}
};


private void startShuffling() {
handler.postDelayed(runnable, 2000);
};


private void initPoints() {
points = new ImageView[views.size()];
// 动态添加小点
for (int i = 0; i < views.size(); i++) {
LinearLayout.LayoutParams llp = new LinearLayout.LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
ImageView point_view = new ImageView(context);// ne 一个ImageView
point_view.setPadding(10, 5, 10, 5);
point_view.setClickable(true);
llp.gravity = Gravity.CENTER_VERTICAL;
point_view.setImageDrawable(pointDrawableNoraml);// 设置资源
pointLinearLayout.addView(point_view, llp); // 最后一步,添加控件到布局中
}


// 循环取得小点图片
for (int i = 0; i < views.size(); i++) {
// 得到一个LinearLayout下面的每一个子元素
points[i] = (ImageView) pointLinearLayout.getChildAt(i);
// 默认都设为灰色
points[i].setEnabled(true);
// 给每个小点设置监听
points[i].setOnClickListener(new OnClickListener() {


@Override
public void onClick(View v) {
int positions = (Integer) v.getTag();
setCurView(positions + 1);
setCurDot(positions);
pointIndex = positions;
}


});
// 设置位置tag,方便取出与当前位置对应
points[i].setTag(i);
}


// 当只有一张图片的时候,隐藏那个小点
if (views.size() > 1) {
pointLinearLayout.setVisibility(View.VISIBLE);
} else {
pointLinearLayout.setVisibility(View.GONE);
}


currentIndex = 0;
// 设置为白色,即选中状态
points[0].setEnabled(false);
points[0].setImageDrawable(pointDrawableSelect);


}


protected void setCurDot(int position) {
if (position < 0 || position > views.size() - 1
|| currentIndex == position) {
return;
}
points[position].setEnabled(false);
points[position].setImageDrawable(pointDrawableSelect);// 设置资源
points[currentIndex].setEnabled(true);
points[currentIndex].setImageDrawable(pointDrawableNoraml);
currentIndex = position;
}


protected void setCurView(int position) {
if (position < 0) {
return;
}
imageViewPager.setCurrentItem(position);
}


@Override
public void onPageScrolled(int position, float positionOffset,
int positionOffsetPixels) {
if (isScrolling) {
if (lastValue > positionOffsetPixels) {
// 递减,向右侧滑动
right = true;
left = false;
} else if (lastValue < positionOffsetPixels) {
// 递减,向右侧滑动
right = false;
left = true;
} else if (lastValue == positionOffsetPixels) {
right = left = false;
}
}
lastValue = positionOffsetPixels;
}


@Override
public void onPageScrollStateChanged(int state) {
if (state == 1) {
isScrolling = true;
} else {
isScrolling = false;
}


if (state == 2) {
if (changeViewCallback != null) {
changeViewCallback.changeView(left, right);
}
right = left = false;
}
}


@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
isPlay = false;
break;
case MotionEvent.ACTION_UP:
isPlay = true;
if (right) {
pointIndex = currentIndex;
}
if (left) {
pointIndex = currentIndex + 1;
}
break;
default:
break;
}
return false;
}


/**
* 得到是否向右侧滑动

* @return true 为右滑动
*/
public boolean getMoveRight() {
return right;
}


/**
* 得到是否向左侧滑动

* @return true 为左做滑动
*/
public boolean getMoveLeft() {
return left;
}


/**
* 滑动状态改变回调

* @author fuxingkai

*/
public interface ChangeViewCallback {
/**
* 切换视图 ?决定于left和right 。

* @param left
* @param right
*/
public void changeView(boolean left, boolean right);
}


/**
* set ...

* @param callback
*/
public void setChangeViewCallback(ChangeViewCallback callback) {
changeViewCallback = callback;
}


public class AdapterCycle extends PagerAdapter {
private LinkedList<View> mViews; // 新view集合


public AdapterCycle(Context context, LinkedList<View> mOldViews) {
if (mOldViews != null) {
// 无论是否多于1个,都要初始化第一个(index:0)
mViews = new LinkedList<View>();


final ImageView view = (ImageView) mOldViews.get(mOldViews
.size() - 1);
ImageView imageView = new ImageView(context);
imageView.setImageDrawable(view.getDrawable());
mViews.add(imageView);
// 注意,如果不只1个,mViews比mList多两个(头尾各多一个)
// 假设:mList为mList[0~N-1], mViews为mViews[0~N+1]
// mViews[0]放mList[N-1], mViews[i]放mList[i-1],
// mViews[N+1]放mList[0]
// mViews[1~N]用于循环;首位之前的mViews[0]和末尾之后的mViews[N+1]用于跳转
// 首位之前的mViews[0],跳转到末尾(N);末位之后的mViews[N+1],跳转到首位(1)
if (mOldViews.size() > 1) { // 多于1个要循环
for (int i = 0; i < mOldViews.size(); i++) { // 中间的N个(index:1~N)
final View view1 = mOldViews.get(i);
mViews.add(view1);
}
// 最后一个(index:N+1)
final ImageView view2 = (ImageView) mOldViews.get(0);
ImageView imageView1 = new ImageView(context);
imageView.setImageDrawable(view2.getDrawable());
mViews.add(imageView1);
}
}
}


@Override
public int getCount() {
return mViews.size();
}


@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}


@Override
public void destroyItem(ViewGroup container, int position, Object object) {
((ViewPager) container).removeView(mViews.get(position));
}


@Override
public Object instantiateItem(ViewGroup container, int position) {
View view = mViews.get(position);
container.addView(view);
return view;
}
}


}

首先是继承一个RelativeLayout类,然后实现他的OnPageChangeListener, OnTouchListener这两个监听。然后在构造函数中调用initView()方法,初始化轮播图里面的view


把刚才写的自定义轮播图的布局加载进来。


然后初始化小点的两个状态对应的图片,记得recycle掉。

通过setViews方法,把要轮播的图片加载进来

public void setViews(LinkedList<View> listViews) {
views = listViews;
initPoints();
setViewPageAdapter(new AdapterCycle(context, views));
}

然后在这里面初始化小点

private void initPoints() {
points = new ImageView[views.size()];
// 动态添加小点
for (int i = 0; i < views.size(); i++) {
LinearLayout.LayoutParams llp = new LinearLayout.LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
ImageView point_view = new ImageView(context);// ne 一个ImageView
point_view.setPadding(10, 5, 10, 5);
point_view.setClickable(true);
llp.gravity = Gravity.CENTER_VERTICAL;
point_view.setImageDrawable(pointDrawableNoraml);// 设置资源
pointLinearLayout.addView(point_view, llp); // 最后一步,添加控件到布局中
}


// 循环取得小点图片
for (int i = 0; i < views.size(); i++) {
// 得到一个LinearLayout下面的每一个子元素
points[i] = (ImageView) pointLinearLayout.getChildAt(i);
// 默认都设为灰色
points[i].setEnabled(true);
// 给每个小点设置监听
points[i].setOnClickListener(new OnClickListener() {


@Override
public void onClick(View v) {
int positions = (Integer) v.getTag();
setCurView(positions + 1);
setCurDot(positions);
pointIndex = positions;
}


});
// 设置位置tag,方便取出与当前位置对应
points[i].setTag(i);
}


// 当只有一张图片的时候,隐藏那个小点
if (views.size() > 1) {
pointLinearLayout.setVisibility(View.VISIBLE);
} else {
pointLinearLayout.setVisibility(View.GONE);
}


currentIndex = 0;
// 设置为白色,即选中状态
points[0].setEnabled(false);
points[0].setImageDrawable(pointDrawableSelect);

}

通过计算图片的个数,然后在pointLinearLayout里面add进对应的小点每个小点设置为可点击图片设置为正常状态的小点。然后循环读取每个小点,给他们设置点击事件通过settag方法来辨别小点,然后里面的setCurview方法是用来设置viewpage当前显示的是那个页面,setcurDot是用来设置当前小点显示的是哪个点,判断一下,如果只用一张图片,就不显示小点,设置当前第一个小点为选择状态。其中setcurView和setcurDot方法里面的东西很简单就不多说

protected void setCurDot(int position) {
if (position < 0 || position > views.size() - 1
|| currentIndex == position) {
return;
}
points[position].setEnabled(false);
points[position].setImageDrawable(pointDrawableSelect);// 设置资源
points[currentIndex].setEnabled(true);
points[currentIndex].setImageDrawable(pointDrawableNoraml);
currentIndex = position;
}


protected void setCurView(int position) {
if (position < 0) {
return;
}
imageViewPager.setCurrentItem(position);
}

在上setViews里面还为viewpage设置了适配器setViewPageAdapter,其中AdapterCycle适配器主要功能为轮播的图片在viewpage里面显示的时候让他们达到循环的效果。如何让它达到这种效果呢,就是让viewpage显示的页面比预期的页面多两个页面,从网上盗用一张图片来讲可能大家比较清楚

就是说如果是在预期第一个页面继续向左滑动,那显示的是最后一个页面;如果是在预期的最后一个页面继续向右滑动的话,那显示的就是第一页面。也就是说真是viewpage比我们看到页面多两个页面。那两个页面分别是第一个和最后一个。不太明白的朋友可以自己看着图片琢磨一下。所以我们在viewpage的适配器里面要为传过来的图片加上前后加上一张图片。下面就是添加的代码。

public AdapterCycle(Context context, LinkedList<View> mOldViews) {
if (mOldViews != null) {
// 无论是否多于1个,都要初始化第一个(index:0)
mViews = new LinkedList<View>();


final ImageView view = (ImageView) mOldViews.get(mOldViews
.size() - 1);
ImageView imageView = new ImageView(context);
imageView.setImageDrawable(view.getDrawable());
mViews.add(imageView);
// 注意,如果不只1个,mViews比mList多两个(头尾各多一个)
// 假设:mList为mList[0~N-1], mViews为mViews[0~N+1]
// mViews[0]放mList[N-1], mViews[i]放mList[i-1],
// mViews[N+1]放mList[0]
// mViews[1~N]用于循环;首位之前的mViews[0]和末尾之后的mViews[N+1]用于跳转
// 首位之前的mViews[0],跳转到末尾(N);末位之后的mViews[N+1],跳转到首位(1)
if (mOldViews.size() > 1) { // 多于1个要循环
for (int i = 0; i < mOldViews.size(); i++) { // 中间的N个(index:1~N)
final View view1 = mOldViews.get(i);
mViews.add(view1);
}
// 最后一个(index:N+1)
final ImageView view2 = (ImageView) mOldViews.get(0);
ImageView imageView1 = new ImageView(context);
imageView.setImageDrawable(view2.getDrawable());
mViews.add(imageView1);
}
}
}

然后为view怕个添加OnPageChangeListener和OnTouchListener监听,设置当前显示的是第二个页面。然后开启定时轮播。


轮播的时候判断当前是否是轮播到了第一个,如果是第一个,就把viewpage设置显示到倒数第二个,如果轮播到倒数第一个,就把viewpage设置显示到第二个。如果不是第一个和倒数第一个的话,就设置当前小点的显示位置。同样在viewpage的onpageSelected方法里面也是


考虑到当滑动的时候就不能让他自定轮播,所以在onTouch里面分别设置isPlay是否可以自定播放。


同时如果手动滑动完之后要让自定滑动不错乱,所以定义了两个字段分别用来标识当前执行的向左还是向右滑动。然后为pointIndex分别赋不同的值

其中判断right和left是通过定义一个ChangeViewCallback接口来切换当前是right还是left的状态。主要代码如下:


自定义的轮播图主要部分基本讲完,现在我们开始调用。在activity里面通过为自定义的轮播图viewsetviews来开启轮播。

package com.demo.shufflingpicture;


import java.util.LinkedList;


import android.app.Activity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;


public class MainActivity extends Activity {
private ShufflingPicture shufflingPicture;
private LinkedList<View> views;
private LayoutInflater mInflater; // 用于解XML
private int[] strings = new int[] { R.drawable.image_1, R.drawable.image_2,
R.drawable.image_3, R.drawable.image_4 , R.drawable.image_5};

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);


setContentView(R.layout.activity_main);
initView();

}
private void initView() {
mInflater = LayoutInflater.from(this);
views = new LinkedList<View>();
for(int i=0;i<strings.length;i++){
ImageView view = (ImageView) mInflater.inflate(
R.layout.imageview, null);
view.setImageResource(strings[i]);
views.add(view);
}
shufflingPicture = (ShufflingPicture) findViewById(R.id.shuffling_picture);
shufflingPicture.setViews(views);
}


}

activity的布局文件如下:activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:my="http://schemas.android.com/apk/res/com.demo.shufflingpicture"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.demo.shufflingpicture.MainActivity" >


    <com.demo.shufflingpicture.ShufflingPicture
        android:id="@+id/shuffling_picture"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        my:pointDrawbleNormal="@drawable/point_normal"
        my:pointDrawbleSelect="@drawable/point_select"
        android:layout_gravity="center_vertical|center_horizontal"/>


</RelativeLayout>

主要就是为自定义的属性添加图片。

到这里,我们自定义轮播图view已经讲完,看不太懂的朋友可以问我,谢谢大家,

源码的下载地址:http://download.csdn.net/detail/kai_1215/8759761

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值