您的位置:首页 > 其它

循环轮播图

2015-06-01 11:21 113 查看
循环轮播图,想必大家都在很多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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: