这几天看了《Android开发艺术探索》这本书真的是挺不错的,学了自定义View之后打算动手实践一个轮播图控件,网上有很多实现的方法,我最后实现起来跟他们也基本上都是大同小异,主要我也是为了练练动手能力。先来个效果图,图片是在百度搜的正经图片,项目代码的链接放在文章的最后
实现轮播图的方式大体上我看到了三种,一是使用安卓的Gallery控件来实现,二是使用HorizontalScrollView,三是使用ViewPager来实现,Gallery控件现在已经不推荐使用了,在源码中我们也看到了,现在推荐使用的是后面的两种,而我实现的方式选择了第三种ViewPager,整个实现流程可以大致分为一下三个部分:
1.AutoSlideView 也是轮播图控件的实现,这里我的控件继承自ViewGoup的FrameLayout子类,同时需要一个xml的布局文件来定义控件的样式,里面主要包括了ViewPager和下面指示器圆点的容器。
2.Adapter的实现,需要给AutoSlideView中的ViewPager指定适配器,来指定轮播图中需要展示出来的View控件,同时也需要每个具体ItemView的xml布局文件,ViewPager的Adapter和ListView的Adapter使用起来思想上都不会相差太多。
3.SlideInfo 数据类的设计,因为实际的开发中轮播图需要的图片之类的信息是需要从服务器上拉取的,需要对数据进行一次封装。
//由于我这个不涉及到具体的业务,所以这个类仅仅只是设置了一个图片的URL
public class SlideInfo {
String image_url ="dd";
public SlideInfo(){}
public SlideInfo(String url){
this.image_url = url;
}
public String getImage_url() {
return image_url;
}
}
首先PagerAdapter使用时需要重写四个方法,考虑到如果轮播图实用在不同的界面上,可能会有多种不同的ItemView 如果每次都重写一个PagerAdapter 会造成大量重复代码的编写,可以考虑编写一个抽象的适配器,把获取View单独让子类去实现
package com.humorousz.myapplication.adapter;
import android.support.v4.view.PagerAdapter;
import android.view.View;
import android.view.ViewGroup;
import java.util.ArrayList;
import java.util.List;
/**
* Created by zhangzhiquan on 2016/7/14.
*/
public abstract class AutoSlideViewAdapterBase extends PagerAdapter {
private List mList;
public AutoSlideViewAdapterBase() {
mList = new ArrayList<>();
}
@Override
public int getCount() {
/**
* 这里需要说明一点,因为我们需要循环滑动
* 但是ViewPager本身并不支持,所以我们需要给
* ViewPager的页面设置成int最大值造成循环滑动
* 的假象,具体可以在网上搜索ViewPager循环滑动
* 可能会有更好的实现方式
*/
return Integer.MAX_VALUE;
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
View v;
/**
* 我在实验的时候发现有时候size和position的差值大于一
* 造成越界,所以在这添加从size到实际位置的View
*/
if (mList.size() <= (position % getSize())) {
for(int i=mList.size() ; i<=position%getSize();++i){
mList.add(getView(container,i));
}
}
v = mList.get(position % getSize());
if(v.getParent() != null){
container.removeView(v);
}
container.addView(v);
return v;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView(mList.get(position % getSize()));
}
/**
*抽象的方法,用于获取实际需要显示的View
*/
public abstract View getView(ViewGroup container,int position);
/**
*用于获取实际ItemView的数量
*/
public abstract int getSize();
}
package com.humorousz.myapplication.adapter;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import com.humorousz.myapplication.R;
import com.humorousz.myapplication.data.SlideInfo;
import com.nostra13.universalimageloader.core.ImageLoader;
import java.util.List;
/**
* Created by zhangzhiquan on 2016/7/14.
*/
public class SlideAdapter extends AutoSlideViewAdapterBase {
List mSlides;
Context mContext;
public SlideAdapter(Context context,List list){
mContext = context;
mSlides = list;
}
/**
* 实现了获取Viwe的方法
* 使用了ImageLoder来获取图片显示到ImageView上
*/
@Override
public View getView(ViewGroup container,int position) {
ImageView imageView;
View v = LayoutInflater.from(mContext)
.inflate(R.layout.layout_slide_item,null);
imageView = (ImageView)v.findViewById(R.id.image);
ImageLoader.getInstance().
displayImage(mSlides.get(position).getImage_url(),imageView);
return v;
}
@Override
public int getSize() {
return mSlides.size();
}
}
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ImageView
android:id="@+id/image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY"/>
LinearLayout>
<com.humorousz.myapplication.widget.AutoSlideView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/my_slide_view"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v4.view.ViewPager
android:id="@+id/views_container"
android:layout_width="match_parent"
android:layout_height="@dimen/auto_slide_view_height">android.support.v4.view.ViewPager>
<LinearLayout
android:id="@+id/indicator"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:layout_marginBottom="@dimen/auto_slide_view_point_container_margin"
android:orientation="vertical"
android:visibility="visible">
<LinearLayout
android:id="@+id/point_container"
android:layout_width="match_parent"
android:layout_height="@dimen/auto_slide_view_point_container_height"
android:gravity="center_horizontal"
android:orientation="horizontal" />
LinearLayout>
com.humorousz.myapplication.widget.AutoSlideView>
package com.humorousz.myapplication.widget;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import com.humorousz.myapplication.R;
import com.humorousz.myapplication.adapter.AutoSlideViewAdapterBase;
import java.util.Timer;
import java.util.TimerTask;
/**
* Created by zhangzhiquan on 2016/7/14.
*/
public class AutoSlideView extends FrameLayout implements ViewPager.OnPageChangeListener {
private ViewPager mViewsContainer;
private LinearLayout mPointContainer;
private AutoSlideViewAdapterBase mAdapter;
private int mPointCount;
private ImageView[] mPoints;
private int mLastPos;
private Timer mTimer;
private TimerTask mTask;
private boolean isTouch = false;
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
mViewsContainer.setCurrentItem(mViewsContainer.getCurrentItem() + 1);
}
};
public AutoSlideView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
init();
}
private void init() {
mViewsContainer = (ViewPager) findViewById(R.id.views_container);
mPointContainer = (LinearLayout) findViewById(R.id.point_container);
mViewsContainer.addOnPageChangeListener(this);
}
public void setData(AutoSlideViewAdapterBase adapter) {
mAdapter = adapter;
mPointCount = mAdapter.getSize();
mViewsContainer.setAdapter(mAdapter);
initPoint();
/**
*这样做是可以保证初始情况下可以向左滑动,
*但是如果有毅力的话,还是能左滑到尽头的
*这个方法也是在别人的博客里看见的
*/
mViewsContainer.setCurrentItem(mPointCount * 100);
startAutoSlide();
}
private void initPoint() {
if (mPointCount == 0)
return;
mPoints = new ImageView[mPointCount];
for (int i = 0; i < mPointCount; i++) {
ImageView view = new ImageView(getContext());
view.setImageResource(R.mipmap.point_normal);
mPointContainer.addView(view);
mPoints[i] = view;
}
if (mPoints[0] != null) {
mPoints[0].setImageResource(R.mipmap.point_selected);
}
mLastPos = 0;
}
private void changePoint(int curPos) {
if (mLastPos == curPos)
return;
mPoints[curPos].setImageResource(R.mipmap.point_selected);
mPoints[mLastPos].setImageResource(R.mipmap.point_normal);
mLastPos = curPos;
}
private void startAutoSlide() {
if (mTimer == null)
mTimer = new Timer();
if (mTask != null) {
mTask.cancel();
mTask = null;
}
mTask = new TimerTask() {
@Override
public void run() {
/**
*如果正在触摸就暂时结束自动滑动
*/
if (isTouch) {
return;
}
handler.sendEmptyMessage(0x00);
}
};
mTimer.schedule(mTask, 300, 3000);
}
public void cancelAutoSlide() {
if (mTask != null) {
mTask.cancel();
mTask = null;
}
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
changePoint(position % mPointCount);
}
@Override
public void onPageScrollStateChanged(int state) {
}
/**
*isTouch变量用于是否用户在点击或者滑动
*可根据这个标记来暂时停止自动滑动
*/
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
isTouch = true;
break;
case MotionEvent.ACTION_UP:
isTouch = false;
break;
}
return super.dispatchTouchEvent(ev);
}
}
package com.humorousz.myapplication;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import com.humorousz.myapplication.adapter.SlideAdapter;
import com.humorousz.myapplication.data.SlideInfo;
import com.humorousz.myapplication.widget.AutoSlideView;
import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.ImageLoaderConfiguration;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
AutoSlideView mAutoSlideView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ImageLoader.getInstance().
init(ImageLoaderConfiguration.createDefault(this));
mAutoSlideView = (AutoSlideView) LayoutInflater.from(this).
inflate(R.layout.layout_auto_slide_view,null);
List list = new ArrayList<>();
list.add(new SlideInfo("http://g.hiphotos.baidu.com/image/pic/item/" +
"f3d3572c11dfa9ecfc13ccc066d0f703918fc12c.jpg"));
list.add(new SlideInfo("http://img5.imgtn.bdimg.com/it/" +
"u=1944136054,210950671&fm=21&gp=0.jpg"));
list.add(new SlideInfo("http://img05.tooopen.com/images/" +
"20140728/sy_67568071862.jpg"));
list.add(new SlideInfo("http://img4.imgtn.bdimg.com/it/" +
"u=652859225,1742711825&fm=21&gp=0.jpg"));
list.add(new SlideInfo("http://img.taopic.com/uploads/" +
"allimg/140517/240456-14051G5163057.jpg"));
mAutoSlideView.setData(new SlideAdapter(this, list));
addContentView(mAutoSlideView,
new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT));
}
}
ok,到此整个控件就完成了,还有不足的地方就是,控件的高度没有能够在使用时设置,可以进一步将AutoSlideView进行抽象然后将高度进行设置,也可以在使用时设置LayoutParams,wrap_content应该不是很常用,因为实际开发过程中布局的宽度设计同学都会给标注好的
下载连接:Android轮播图