=UTF-8
3 |
--------------------------------------------------------------------------------
/.settings/org.eclipse.jdt.core.prefs:
--------------------------------------------------------------------------------
1 | eclipse.preferences.version=1
2 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
3 | org.eclipse.jdt.core.compiler.compliance=1.6
4 | org.eclipse.jdt.core.compiler.source=1.6
5 |
--------------------------------------------------------------------------------
/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
12 |
13 |
18 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 关注finddreams,一起分享,一起进步: http://blog.csdn.net/finddreams/article/details/44619589
2 |
3 | 今天给大家带来一点干货,就是横向循环滚动的广告条。
4 | 有点类似淘宝的banner广告位,可以手势滑动,也会依据固定时间间隔自动滚动,滑到尽头时会一直循环。过渡非常的平滑。从网络中获取图片,并缓存到SD卡当中,做为没有网络的时候可以显示,提高用户的体验,点击每个广告条会进入相应的url界面,封装的好,使用起来非常的方便。
5 | 下面就让我们来看看运行的效果图,看看是不是你要找的:
6 |
7 | 
8 |
9 |
10 | 记得之前为了做这样的效果,从网络上找了很多的源码。结果发现有很多的源码的效果要么是滑到尽头到第一个广告时,会从第二,三个广告中穿过,过渡非常的不平滑。要么就是封装的不够好,平滑效果是实现了,但却不能点击,或者说不能从网络上加载图片。
11 | 这两种情况都是不符合我们商业项目开发的。因为广告条会变,所以必须从后台获取图片的url地址,以及对应的广告信息的Url。
12 | 那今天给大家带来的干货就是为了解决这些问题,还你一个完美的广告条的实现。
13 | 我们都知道,广告条的效果一般的做法就是使用ViewPager加上一个定时器Time,TimerTask或者是handler来定时的滚动广告图片,同时控制广告的指示小点点的选中与未选中时显示的状态图片。
14 |
15 |
16 | 今天分享的广告条的做法有点不一样,但是效果确相比ViewPager更加的有趣。我们使用的是ViewFlow。
17 | **1.首先自定义一个ViewFlow类:**
18 | 因为这个类的代码量比较大,出于篇幅考虑,代码我就不贴了,见最下面的下载链接,下载源码可以自己去研究一下,具体的实现原理。
19 |
20 | **2.然后定义一个CircleFlowIndicator类**
21 | 这个类是来控制广告条中小圆点的滚动,从效果图中我们可以看到,三个小圆点的滚动也是非常的平滑的移动过去,让人感觉很流畅,比世面上很多的App中实现的那种广告小圆点的效果也要好很多。
22 | 具体代码依然见源码;
23 |
24 | **3.接下来我们就在布局文件中开始使用了**
25 | ```
26 |
32 |
33 |
38 |
39 |
43 |
44 |
51 |
52 |
64 |
65 |
66 |
67 |
68 | ```
69 |
70 | **4.然后我们就可以在Activity中调用了,具体的代码是:**
71 | ```
72 | private void initView() {
73 | mViewFlow = (ViewFlow) findViewById(R.id.viewflow);
74 | mFlowIndicator = (CircleFlowIndicator) findViewById(R.id.viewflowindic);
75 | }
76 |
77 | private void initBanner(ArrayList imageUrlList) {
78 |
79 | mViewFlow.setAdapter(new ImagePagerAdapter(this, imageUrlList,
80 | linkUrlArray, titleList).setInfiniteLoop(true));
81 | mViewFlow.setmSideBuffer(imageUrlList.size()); // 实际图片张数,
82 | mViewFlow.setFlowIndicator(mFlowIndicator);
83 | mViewFlow.setTimeSpan(4500);
84 | mViewFlow.setSelection(imageUrlList.size() * 1000); // 设置初始位置
85 | mViewFlow.startAutoFlowTimer(); // 启动自动播放
86 | }
87 | }
88 | ```
89 | **5.有一个很关键的就是ImagePagerAdapter这个适配器,因为加载网络图片是在这个类里实现的,还有广告条的点击,进入一个Web界面的实现。在这里加载网络图片我们使用了一个很火的开源项目,UniversalImageLoader(异步加载网络图片) ,相信大家也并不陌生了。**
90 | ImagePagerAdapter.class 类:
91 |
92 |
93 | **6.点击广告条进入一个带进度条的WebView的Activity,这个效果我之前的博客中就有介绍过,详情可见:[仿微信中加载网页时带线行进度条的WebView的实现 ](http://blog.csdn.net/finddreams/article/details/44172)**
94 |
95 | 到这里就可以实现完美的广告条了,哦,对了因为是加载网络图片,所以加的要加上访问网络权限哦,不然就显示不了网络图片,还有因为用到了UniversalImageLoader把图片离线缓存到SD卡当中,所以也是加权限的。
96 | ```
97 |
98 |
99 | ```
100 | 终于实现了循环滚动,平滑过渡的广告条效果,真心让人松了一口气。为了不让初学者继续走前人的崎岖路,快速的进步。本着分享开源的精神,把源码发布出来,供大家学习,记住 分享开源会让你学到更多!
101 |
102 |
103 |
104 |
105 |
106 |
107 |
--------------------------------------------------------------------------------
/ic_launcher-web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/finddreams/ADBannerUI/5d22e8d4f6f2046bb005e443a7bb7dd96d3082a7/ic_launcher-web.png
--------------------------------------------------------------------------------
/libs/android-support-v4.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/finddreams/ADBannerUI/5d22e8d4f6f2046bb005e443a7bb7dd96d3082a7/libs/android-support-v4.jar
--------------------------------------------------------------------------------
/libs/universal-image-loader-1.9.3.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/finddreams/ADBannerUI/5d22e8d4f6f2046bb005e443a7bb7dd96d3082a7/libs/universal-image-loader-1.9.3.jar
--------------------------------------------------------------------------------
/proguard-project.txt:
--------------------------------------------------------------------------------
1 | # To enable ProGuard in your project, edit project.properties
2 | # to define the proguard.config property as described in that file.
3 | #
4 | # Add project specific ProGuard rules here.
5 | # By default, the flags in this file are appended to flags specified
6 | # in ${sdk.dir}/tools/proguard/proguard-android.txt
7 | # You can edit the include path and order by changing the ProGuard
8 | # include property in project.properties.
9 | #
10 | # For more details, see
11 | # http://developer.android.com/guide/developing/tools/proguard.html
12 |
13 | # Add any project specific keep options here:
14 |
15 | # If your project uses WebView with JS, uncomment the following
16 | # and specify the fully qualified class name to the JavaScript interface
17 | # class:
18 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
19 | # public *;
20 | #}
21 |
--------------------------------------------------------------------------------
/project.properties:
--------------------------------------------------------------------------------
1 | # This file is automatically generated by Android Tools.
2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
3 | #
4 | # This file must be checked in Version Control Systems.
5 | #
6 | # To customize properties used by the Ant build system edit
7 | # "ant.properties", and override values to adapt the script to your
8 | # project structure.
9 | #
10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
12 |
13 | # Project target.
14 | target=android-16
15 |
--------------------------------------------------------------------------------
/res/anim/in_bottomtop.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
13 |
14 |
--------------------------------------------------------------------------------
/res/anim/in_topbottom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/res/anim/out_bottomtop.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/res/anim/out_topbottom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/finddreams/ADBannerUI/5d22e8d4f6f2046bb005e443a7bb7dd96d3082a7/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/meinv.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/finddreams/ADBannerUI/5d22e8d4f6f2046bb005e443a7bb7dd96d3082a7/res/drawable-hdpi/meinv.jpg
--------------------------------------------------------------------------------
/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/finddreams/ADBannerUI/5d22e8d4f6f2046bb005e443a7bb7dd96d3082a7/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/finddreams/ADBannerUI/5d22e8d4f6f2046bb005e443a7bb7dd96d3082a7/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/notice_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/finddreams/ADBannerUI/5d22e8d4f6f2046bb005e443a7bb7dd96d3082a7/res/drawable-xhdpi/notice_icon.png
--------------------------------------------------------------------------------
/res/drawable-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/finddreams/ADBannerUI/5d22e8d4f6f2046bb005e443a7bb7dd96d3082a7/res/drawable-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable/progress_bar_states.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | -
6 |
7 |
8 |
9 |
14 |
15 |
16 | -
17 |
18 |
19 |
20 |
21 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/res/layout/activity_baseweb.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
13 |
14 |
--------------------------------------------------------------------------------
/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
13 |
14 |
18 |
19 |
26 |
27 |
39 |
40 |
41 |
47 |
48 |
--------------------------------------------------------------------------------
/res/layout/layout_notice.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
13 |
14 |
22 |
23 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/res/layout/notice_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 | 64dp
9 |
10 |
11 |
--------------------------------------------------------------------------------
/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #00000000
4 | #fff0f0f0
5 |
6 |
--------------------------------------------------------------------------------
/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 16dp
5 | 16dp
6 | 12.0dip
7 | 15.0dip
8 | 8dip
9 |
10 |
11 |
--------------------------------------------------------------------------------
/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | ADBanner
5 | Hello world!
6 | Settings
7 |
8 |
9 |
--------------------------------------------------------------------------------
/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
14 |
15 |
--------------------------------------------------------------------------------
/src/com/finddreams/adbanner/BaseWebActivity.java:
--------------------------------------------------------------------------------
1 | package com.finddreams.adbanner;
2 |
3 | import android.app.Activity;
4 | import android.content.Intent;
5 | import android.os.Bundle;
6 | import android.widget.ProgressBar;
7 |
8 |
9 |
10 | /**
11 | * @Description:WebView界面,带自定义进度条显示
12 | * @author http://blog.csdn.net/finddreams
13 | */
14 | public class BaseWebActivity extends Activity {
15 |
16 | protected ProgressWebView mWebView;
17 | private ProgressBar web_progressbar;
18 |
19 | @Override
20 | protected void onCreate(Bundle savedInstanceState) {
21 | super.onCreate(savedInstanceState);
22 | setContentView(R.layout.activity_baseweb);
23 |
24 | mWebView = (ProgressWebView) findViewById(R.id.baseweb_webview);
25 | mWebView.getSettings().setJavaScriptEnabled(true);
26 | initData();
27 | }
28 |
29 | protected void initData() {
30 | Intent intent = getIntent();
31 | Bundle bundle = intent.getExtras();
32 | String url = bundle.getString("url");
33 |
34 | // if(!TextUtils.isEmpty(url)&&TextUtils.isEmpty(title)){
35 | mWebView.loadUrl(url);
36 |
37 | // }
38 |
39 | }
40 |
41 | @Override
42 | protected void onDestroy() {
43 | // TODO Auto-generated method stub
44 | super.onDestroy();
45 | mWebView = null;
46 |
47 | }
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/src/com/finddreams/adbanner/ImagePagerAdapter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 trinea.cn All right reserved. This software is the confidential and proprietary information of
3 | * trinea.cn ("Confidential Information"). You shall not disclose such Confidential Information and shall use it only in
4 | * accordance with the terms of the license agreement you entered into with trinea.cn.
5 | */
6 | package com.finddreams.adbanner;
7 |
8 | import java.util.List;
9 |
10 | import android.content.Context;
11 | import android.content.Intent;
12 | import android.graphics.Bitmap;
13 | import android.os.Bundle;
14 | import android.text.TextUtils;
15 | import android.view.View;
16 | import android.view.View.OnClickListener;
17 | import android.view.ViewGroup;
18 | import android.widget.BaseAdapter;
19 | import android.widget.ImageView;
20 | import android.widget.Toast;
21 |
22 | import com.nostra13.universalimageloader.core.DisplayImageOptions;
23 | import com.nostra13.universalimageloader.core.ImageLoader;
24 | import com.nostra13.universalimageloader.core.ImageLoaderConfiguration;
25 |
26 | /**
27 | * @Description: 图片适配器
28 | * @author http://blog.csdn.net/finddreams
29 | */
30 | public class ImagePagerAdapter extends BaseAdapter {
31 |
32 | private Context context;
33 | private List imageIdList;
34 | private List linkUrlArray;
35 | private List urlTitlesList;
36 | private int size;
37 | private boolean isInfiniteLoop;
38 | private ImageLoader imageLoader;
39 | private DisplayImageOptions options;
40 |
41 | public ImagePagerAdapter(Context context, List imageIdList,
42 | List urllist, List urlTitlesList) {
43 | this.context = context;
44 | this.imageIdList = imageIdList;
45 | if (imageIdList != null) {
46 | this.size = imageIdList.size();
47 | }
48 | this.linkUrlArray = urllist;
49 | this.urlTitlesList = urlTitlesList;
50 | isInfiniteLoop = false;
51 | // 初始化imageLoader 否则会报错
52 | imageLoader = ImageLoader.getInstance();
53 | imageLoader.init(ImageLoaderConfiguration.createDefault(context));
54 | options = new DisplayImageOptions.Builder()
55 | .showStubImage(R.drawable.ic_launcher) // 设置图片下载期间显示的图片
56 | .showImageForEmptyUri(R.drawable.meinv) // 设置图片Uri为空或是错误的时候显示的图片
57 | .showImageOnFail(R.drawable.meinv) // 设置图片加载或解码过程中发生错误显示的图片
58 | .cacheInMemory(true) // 设置下载的图片是否缓存在内存中
59 | .cacheOnDisc(true) // 设置下载的图片是否缓存在SD卡中
60 | .build();
61 |
62 | }
63 |
64 | @Override
65 | public int getCount() {
66 | // Infinite loop
67 | return isInfiniteLoop ? Integer.MAX_VALUE : imageIdList.size();
68 | }
69 |
70 | /**
71 | * get really position
72 | *
73 | * @param position
74 | * @return
75 | */
76 | private int getPosition(int position) {
77 | return isInfiniteLoop ? position % size : position;
78 | }
79 |
80 | @Override
81 | public View getView(final int position, View view, ViewGroup container) {
82 | final ViewHolder holder;
83 | if (view == null) {
84 | holder = new ViewHolder();
85 | view = holder.imageView = new ImageView(context);
86 | holder.imageView
87 | .setLayoutParams(new ViewGroup.LayoutParams(-1, -1));
88 | holder.imageView.setScaleType(ImageView.ScaleType.FIT_XY);
89 | view.setTag(holder);
90 | } else {
91 | holder = (ViewHolder) view.getTag();
92 | }
93 |
94 | imageLoader.displayImage(
95 | (String) this.imageIdList.get(getPosition(position)),
96 | holder.imageView, options);
97 |
98 | holder.imageView.setOnClickListener(new OnClickListener() {
99 |
100 | @Override
101 | public void onClick(View arg0) {
102 | String url = linkUrlArray.get(ImagePagerAdapter.this
103 | .getPosition(position));
104 | String title = urlTitlesList.get(ImagePagerAdapter.this
105 | .getPosition(position));
106 | /*
107 | * if (TextUtils.isEmpty(url)) {
108 | * holder.imageView.setEnabled(false); return; }
109 | */
110 | Bundle bundle = new Bundle();
111 |
112 | bundle.putString("url", url);
113 | bundle.putString("title", title);
114 | Intent intent = new Intent(context, BaseWebActivity.class);
115 | intent.putExtras(bundle);
116 |
117 | context.startActivity(intent);
118 | Toast.makeText(context, "点击了第" + getPosition(position) + "美女",
119 | 0).show();
120 |
121 | }
122 | });
123 |
124 | return view;
125 | }
126 |
127 | private static class ViewHolder {
128 |
129 | ImageView imageView;
130 | }
131 |
132 | /**
133 | * @return the isInfiniteLoop
134 | */
135 | public boolean isInfiniteLoop() {
136 | return isInfiniteLoop;
137 | }
138 |
139 | /**
140 | * @param isInfiniteLoop
141 | * the isInfiniteLoop to set
142 | */
143 | public ImagePagerAdapter setInfiniteLoop(boolean isInfiniteLoop) {
144 | this.isInfiniteLoop = isInfiniteLoop;
145 | return this;
146 | }
147 |
148 | @Override
149 | public Object getItem(int arg0) {
150 | // TODO Auto-generated method stub
151 | return arg0;
152 | }
153 |
154 | @Override
155 | public long getItemId(int arg0) {
156 | // TODO Auto-generated method stub
157 | return arg0;
158 | }
159 |
160 | }
161 |
--------------------------------------------------------------------------------
/src/com/finddreams/adbanner/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.finddreams.adbanner;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Timer;
5 | import java.util.TimerTask;
6 |
7 | import android.app.Activity;
8 | import android.content.Intent;
9 | import android.os.Bundle;
10 | import android.util.Log;
11 | import android.view.View;
12 | import android.view.View.OnClickListener;
13 | import android.widget.FrameLayout;
14 | import android.widget.LinearLayout;
15 | import android.widget.TextView;
16 | import android.widget.ViewFlipper;
17 |
18 | import com.finddreams.bannerview.CircleFlowIndicator;
19 | import com.finddreams.bannerview.ViewFlow;
20 |
21 | /**
22 | * @Description:显示广告条的主页
23 | * @author http://blog.csdn.net/finddreams
24 | */
25 | public class MainActivity extends Activity {
26 |
27 | private ViewFlow mViewFlow;
28 | private CircleFlowIndicator mFlowIndicator;
29 | private ArrayList imageUrlList = new ArrayList();
30 | ArrayList linkUrlArray= new ArrayList();
31 | ArrayList titleList= new ArrayList();
32 | private LinearLayout notice_parent_ll;
33 | private LinearLayout notice_ll;
34 | private ViewFlipper notice_vf;
35 | private int mCurrPos;
36 | @Override
37 | protected void onCreate(Bundle savedInstanceState) {
38 | super.onCreate(savedInstanceState);
39 | setContentView(R.layout.activity_main);
40 | initView();
41 | imageUrlList
42 | .add("http://b.hiphotos.baidu.com/image/pic/item/d01373f082025aaf95bdf7e4f8edab64034f1a15.jpg");
43 | imageUrlList
44 | .add("http://g.hiphotos.baidu.com/image/pic/item/6159252dd42a2834da6660c459b5c9ea14cebf39.jpg");
45 | imageUrlList
46 | .add("http://d.hiphotos.baidu.com/image/pic/item/adaf2edda3cc7cd976427f6c3901213fb80e911c.jpg");
47 | imageUrlList
48 | .add("http://g.hiphotos.baidu.com/image/pic/item/b3119313b07eca80131de3e6932397dda1448393.jpg");
49 |
50 | linkUrlArray
51 | .add("http://blog.csdn.net/finddreams/article/details/44301359");
52 | linkUrlArray
53 | .add("http://blog.csdn.net/finddreams/article/details/43486527");
54 | linkUrlArray
55 | .add("http://blog.csdn.net/finddreams/article/details/44648121");
56 | linkUrlArray
57 | .add("http://blog.csdn.net/finddreams/article/details/44619589");
58 | titleList.add("常见Android进阶笔试题");
59 | titleList.add("GridView之仿支付宝钱包首页");
60 | titleList.add("仿手机QQ网络状态条的显示与消失 ");
61 | titleList.add("Android循环滚动广告条的完美实现 ");
62 | initBanner(imageUrlList);
63 | initRollNotice();
64 | }
65 |
66 | private void initView() {
67 | mViewFlow = (ViewFlow) findViewById(R.id.viewflow);
68 | mFlowIndicator = (CircleFlowIndicator) findViewById(R.id.viewflowindic);
69 |
70 | }
71 | private void initRollNotice() {
72 | FrameLayout main_notice = (FrameLayout) findViewById(R.id.main_notice);
73 | notice_parent_ll = (LinearLayout) getLayoutInflater().inflate(
74 | R.layout.layout_notice, null);
75 | notice_ll = ((LinearLayout) this.notice_parent_ll
76 | .findViewById(R.id.homepage_notice_ll));
77 | notice_vf = ((ViewFlipper) this.notice_parent_ll
78 | .findViewById(R.id.homepage_notice_vf));
79 | main_notice.addView(notice_parent_ll);
80 | TimerTask task = new TimerTask() {
81 | @Override
82 | public void run() {
83 | runOnUiThread(new Runnable() {
84 | @Override
85 | public void run() {
86 | moveNext();
87 | Log.d("Task", "下一个");
88 | }
89 | });
90 |
91 | }
92 | };
93 | Timer timer = new Timer();
94 | timer.schedule(task, 0, 4000);
95 | }
96 |
97 | private void moveNext() {
98 | setView(this.mCurrPos, this.mCurrPos + 1);
99 | this.notice_vf.setInAnimation(this, R.anim.in_bottomtop);
100 | this.notice_vf.setOutAnimation(this, R.anim.out_bottomtop);
101 | this.notice_vf.showNext();
102 | }
103 |
104 | private void setView(int curr, int next) {
105 |
106 | View noticeView = getLayoutInflater().inflate(R.layout.notice_item,
107 | null);
108 | TextView notice_tv = (TextView) noticeView.findViewById(R.id.notice_tv);
109 | if ((curr < next) && (next > (titleList.size() - 1))) {
110 | next = 0;
111 | } else if ((curr > next) && (next < 0)) {
112 | next = titleList.size() - 1;
113 | }
114 | notice_tv.setText(titleList.get(next));
115 | notice_tv.setOnClickListener(new OnClickListener() {
116 |
117 | @Override
118 | public void onClick(View arg0) {
119 | Bundle bundle = new Bundle();
120 | bundle.putString("url", linkUrlArray.get(mCurrPos));
121 | bundle.putString("title", titleList.get(mCurrPos));
122 | Intent intent = new Intent(MainActivity.this,
123 | BaseWebActivity.class);
124 | intent.putExtras(bundle);
125 | startActivity(intent);
126 | }
127 | });
128 | if (notice_vf.getChildCount() > 1) {
129 | notice_vf.removeViewAt(0);
130 | }
131 | notice_vf.addView(noticeView, notice_vf.getChildCount());
132 | mCurrPos = next;
133 |
134 | }
135 | private void initBanner(ArrayList imageUrlList) {
136 |
137 | mViewFlow.setAdapter(new ImagePagerAdapter(this, imageUrlList,
138 | linkUrlArray, titleList).setInfiniteLoop(true));
139 | mViewFlow.setmSideBuffer(imageUrlList.size()); // 实际图片张数,
140 | // 我的ImageAdapter实际图片张数为3
141 |
142 | mViewFlow.setFlowIndicator(mFlowIndicator);
143 | mViewFlow.setTimeSpan(4500);
144 | mViewFlow.setSelection(imageUrlList.size() * 1000); // 设置初始位置
145 | mViewFlow.startAutoFlowTimer(); // 启动自动播放
146 | }
147 |
148 | }
149 |
--------------------------------------------------------------------------------
/src/com/finddreams/adbanner/ProgressWebView.java:
--------------------------------------------------------------------------------
1 | package com.finddreams.adbanner;
2 |
3 | import android.content.Context;
4 | import android.graphics.drawable.Drawable;
5 | import android.util.AttributeSet;
6 | import android.webkit.WebView;
7 | import android.widget.ProgressBar;
8 |
9 |
10 | /**
11 | * @Description: 带进度条的WebView
12 | * @author http://blog.csdn.net/finddreams
13 | */
14 | @SuppressWarnings("deprecation")
15 | public class ProgressWebView extends WebView {
16 |
17 | private ProgressBar progressbar;
18 |
19 | public ProgressWebView(Context context, AttributeSet attrs) {
20 | super(context, attrs);
21 | progressbar = new ProgressBar(context, null,
22 | android.R.attr.progressBarStyleHorizontal);
23 | progressbar.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT,
24 | 10, 0, 0));
25 |
26 | Drawable drawable = context.getResources().getDrawable(R.drawable.progress_bar_states);
27 | progressbar.setProgressDrawable(drawable);
28 | addView(progressbar);
29 | // setWebViewClient(new WebViewClient(){});
30 | setWebChromeClient(new WebChromeClient());
31 | //是否支持缩放
32 | getSettings().setSupportZoom(true);
33 | getSettings().setBuiltInZoomControls(true);
34 | }
35 |
36 | public class WebChromeClient extends android.webkit.WebChromeClient {
37 | @Override
38 | public void onProgressChanged(WebView view, int newProgress) {
39 | if (newProgress == 100) {
40 | progressbar.setVisibility(GONE);
41 | } else {
42 | if (progressbar.getVisibility() == GONE)
43 | progressbar.setVisibility(VISIBLE);
44 | progressbar.setProgress(newProgress);
45 | }
46 | super.onProgressChanged(view, newProgress);
47 | }
48 |
49 | }
50 |
51 | @Override
52 | protected void onScrollChanged(int l, int t, int oldl, int oldt) {
53 | LayoutParams lp = (LayoutParams) progressbar.getLayoutParams();
54 | lp.x = l;
55 | lp.y = t;
56 | progressbar.setLayoutParams(lp);
57 | super.onScrollChanged(l, t, oldl, oldt);
58 | }
59 | }
--------------------------------------------------------------------------------
/src/com/finddreams/bannerview/CircleFlowIndicator.java:
--------------------------------------------------------------------------------
1 |
2 | package com.finddreams.bannerview;
3 |
4 |
5 |
6 |
7 | import com.finddreams.adbanner.R;
8 |
9 | import android.content.Context;
10 | import android.content.res.TypedArray;
11 | import android.graphics.Canvas;
12 | import android.graphics.Paint;
13 | import android.graphics.Paint.Style;
14 | import android.os.AsyncTask;
15 | import android.util.AttributeSet;
16 | import android.view.View;
17 | import android.view.animation.Animation;
18 | import android.view.animation.Animation.AnimationListener;
19 | import android.view.animation.AnimationUtils;
20 |
21 | /**
22 | * A FlowIndicator which draws circles (one for each view).
23 | *
24 | * Availables attributes are:
25 | *
26 | * activeColor: Define the color used to draw the active circle (default to white)
27 | *
28 | *
29 | * inactiveColor: Define the color used to draw the inactive circles (default to 0x44FFFFFF)
30 | *
31 | *
32 | * inactiveType: Define how to draw the inactive circles, either stroke or fill (default to stroke)
33 | *
34 | *
35 | * activeType: Define how to draw the active circle, either stroke or fill (default to fill)
36 | *
37 | *
38 | * fadeOut: Define the time (in ms) until the indicator will fade out (default to 0 = never fade out)
39 | *
40 | *
41 | * radius: Define the circle radius (default to 4.0)
42 | *
43 | *
44 | * @author http://blog.csdn.net/finddreams
45 | */
46 | public class CircleFlowIndicator extends View implements FlowIndicator,
47 | AnimationListener {
48 | private static final int STYLE_STROKE = 0;
49 | private static final int STYLE_FILL = 1;
50 |
51 | private float radius = 4;
52 | private float circleSeparation = 2*radius+radius;
53 | private float activeRadius = 0.5f;
54 | private int fadeOutTime = 0;
55 | private final Paint mPaintInactive = new Paint(Paint.ANTI_ALIAS_FLAG);
56 | private final Paint mPaintActive = new Paint(Paint.ANTI_ALIAS_FLAG);
57 | private ViewFlow viewFlow;
58 | private int currentScroll = 0;
59 | private int flowWidth = 0;
60 | private FadeTimer timer;
61 | public AnimationListener animationListener = this;
62 | private Animation animation;
63 | private boolean mCentered = false;
64 |
65 | /**
66 | * Default constructor
67 | *
68 | * @param context
69 | */
70 | public CircleFlowIndicator(Context context) {
71 | super(context);
72 | initColors(0xFFFFFFFF, 0xFFFFFFFF, STYLE_FILL, STYLE_STROKE);
73 | }
74 |
75 | /**
76 | * The contructor used with an inflater
77 | *
78 | * @param context
79 | * @param attrs
80 | */
81 | public CircleFlowIndicator(Context context, AttributeSet attrs) {
82 | super(context, attrs);
83 | // Retrieve styles attributs
84 | TypedArray a = context.obtainStyledAttributes(attrs,
85 | R.styleable.CircleFlowIndicator);
86 |
87 | // Gets the inactive circle type, defaulting to "fill"
88 | int activeType = a.getInt(R.styleable.CircleFlowIndicator_activeType,
89 | STYLE_FILL);
90 |
91 | int activeDefaultColor = 0xFFFFFFFF;
92 |
93 | // Get a custom inactive color if there is one
94 | int activeColor = a
95 | .getColor(R.styleable.CircleFlowIndicator_activeColor,
96 | activeDefaultColor);
97 |
98 | // Gets the inactive circle type, defaulting to "stroke"
99 | int inactiveType = a.getInt(
100 | R.styleable.CircleFlowIndicator_inactiveType, STYLE_STROKE);
101 |
102 | int inactiveDefaultColor = 0x44FFFFFF;
103 | // Get a custom inactive color if there is one
104 | int inactiveColor = a.getColor(
105 | R.styleable.CircleFlowIndicator_inactiveColor,
106 | inactiveDefaultColor);
107 |
108 | // Retrieve the radius
109 | radius = a.getDimension(R.styleable.CircleFlowIndicator_radius, 4.0f);
110 |
111 | circleSeparation = a.getDimension(R.styleable.CircleFlowIndicator_circleSeparation, 2*radius+radius);
112 | activeRadius = a.getDimension(R.styleable.CircleFlowIndicator_activeRadius, 0.5f);
113 | // Retrieve the fade out time
114 | fadeOutTime = a.getInt(R.styleable.CircleFlowIndicator_fadeOut, 0);
115 |
116 | mCentered = a.getBoolean(R.styleable.CircleFlowIndicator_centered, false);
117 |
118 | initColors(activeColor, inactiveColor, activeType, inactiveType);
119 | }
120 |
121 | private void initColors(int activeColor, int inactiveColor, int activeType,
122 | int inactiveType) {
123 | // Select the paint type given the type attr
124 | switch (inactiveType) {
125 | case STYLE_FILL:
126 | mPaintInactive.setStyle(Style.FILL);
127 | break;
128 | default:
129 | mPaintInactive.setStyle(Style.STROKE);
130 | }
131 | mPaintInactive.setColor(inactiveColor);
132 |
133 | // Select the paint type given the type attr
134 | switch (activeType) {
135 | case STYLE_STROKE:
136 | mPaintActive.setStyle(Style.STROKE);
137 | break;
138 | default:
139 | mPaintActive.setStyle(Style.FILL);
140 | }
141 | mPaintActive.setColor(activeColor);
142 | }
143 |
144 | /*
145 | * (non-Javadoc)
146 | *
147 | * @see android.view.View#onDraw(android.graphics.Canvas)
148 | */
149 | @Override
150 | protected void onDraw(Canvas canvas) {
151 | super.onDraw(canvas);
152 | int count = 3;
153 | if (viewFlow != null) {
154 | count = viewFlow.getViewsCount();
155 | }
156 |
157 | //this is the amount the first circle should be offset to make the entire thing centered
158 | float centeringOffset = 0;
159 |
160 | int leftPadding = getPaddingLeft();
161 |
162 | // Draw stroked circles
163 | for (int iLoop = 0; iLoop < count; iLoop++) {
164 | canvas.drawCircle(leftPadding + radius
165 | + (iLoop * circleSeparation) + centeringOffset,
166 | getPaddingTop() + radius, radius, mPaintInactive);
167 | }
168 | float cx = 0;
169 | if (flowWidth != 0) {
170 | // Draw the filled circle according to the current scroll
171 | cx = (currentScroll * circleSeparation) / flowWidth;
172 | }
173 | // The flow width has been upadated yet. Draw the default position
174 | canvas.drawCircle(leftPadding + radius + cx+centeringOffset, getPaddingTop()
175 | + radius, radius + activeRadius, mPaintActive);
176 | }
177 |
178 | /*
179 | * (non-Javadoc)
180 | *
181 | * @see
182 | * org.taptwo.android.widget.ViewFlow.ViewSwitchListener#onSwitched(android
183 | * .view.View, int)
184 | */
185 | @Override
186 | public void onSwitched(View view, int position) {
187 | }
188 |
189 | /*
190 | * (non-Javadoc)
191 | *
192 | * @see
193 | * org.taptwo.android.widget.FlowIndicator#setViewFlow(org.taptwo.android
194 | * .widget.ViewFlow)
195 | */
196 | @Override
197 | public void setViewFlow(ViewFlow view) {
198 | resetTimer();
199 | viewFlow = view;
200 | flowWidth = viewFlow.getWidth();
201 | invalidate();
202 | }
203 |
204 | /*
205 | * (non-Javadoc)
206 | *
207 | * @see org.taptwo.android.widget.FlowIndicator#onScrolled(int, int, int,
208 | * int)
209 | */
210 | @Override
211 | public void onScrolled(int h, int v, int oldh, int oldv) {
212 | setVisibility(View.VISIBLE);
213 | resetTimer();
214 | flowWidth = viewFlow.getWidth();
215 | if(viewFlow.getViewsCount()*flowWidth!=0){
216 | currentScroll = h%(viewFlow.getViewsCount()*flowWidth);
217 | }else {
218 | currentScroll = h;
219 | }
220 | invalidate();
221 | }
222 |
223 | /*
224 | * (non-Javadoc)
225 | *
226 | * @see android.view.View#onMeasure(int, int)
227 | */
228 | @Override
229 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
230 | setMeasuredDimension(measureWidth(widthMeasureSpec),
231 | measureHeight(heightMeasureSpec));
232 | }
233 |
234 | /**
235 | * Determines the width of this view
236 | *
237 | * @param measureSpec
238 | * A measureSpec packed into an int
239 | * @return The width of the view, honoring constraints from measureSpec
240 | */
241 | private int measureWidth(int measureSpec) {
242 | int result = 0;
243 | int specMode = MeasureSpec.getMode(measureSpec);
244 | int specSize = MeasureSpec.getSize(measureSpec);
245 |
246 | // We were told how big to be
247 | if (specMode == MeasureSpec.EXACTLY) {
248 | result = specSize;
249 | }
250 | // Calculate the width according the views count
251 | else {
252 | int count = 3;
253 | if (viewFlow != null) {
254 | count = viewFlow.getViewsCount();
255 | }
256 | float temp = circleSeparation - 2*radius;
257 | result = (int) (getPaddingLeft() + getPaddingRight()
258 | + (count * 2 * radius) + (count - 1) * temp + 1);
259 | // Respect AT_MOST value if that was what is called for by
260 | // measureSpec
261 | if (specMode == MeasureSpec.AT_MOST) {
262 | result = Math.min(result, specSize);
263 | }
264 | }
265 | return result;
266 | }
267 |
268 | /**
269 | * Determines the height of this view
270 | *
271 | * @param measureSpec
272 | * A measureSpec packed into an int
273 | * @return The height of the view, honoring constraints from measureSpec
274 | */
275 | private int measureHeight(int measureSpec) {
276 | int result = 0;
277 | int specMode = MeasureSpec.getMode(measureSpec);
278 | int specSize = MeasureSpec.getSize(measureSpec);
279 |
280 | // We were told how big to be
281 | if (specMode == MeasureSpec.EXACTLY) {
282 | result = specSize;
283 | }
284 | // Measure the height
285 | else {
286 | result = (int) (2 * radius + getPaddingTop() + getPaddingBottom() + 1);
287 | // Respect AT_MOST value if that was what is called for by
288 | // measureSpec
289 | if (specMode == MeasureSpec.AT_MOST) {
290 | result = Math.min(result, specSize);
291 | }
292 | }
293 | return result;
294 | }
295 |
296 | /**
297 | * Sets the fill color
298 | *
299 | * @param color
300 | * ARGB value for the text
301 | */
302 | public void setFillColor(int color) {
303 | mPaintActive.setColor(color);
304 | invalidate();
305 | }
306 |
307 | /**
308 | * Sets the stroke color
309 | *
310 | * @param color
311 | * ARGB value for the text
312 | */
313 | public void setStrokeColor(int color) {
314 | mPaintInactive.setColor(color);
315 | invalidate();
316 | }
317 |
318 | /**
319 | * Resets the fade out timer to 0. Creating a new one if needed
320 | */
321 | private void resetTimer() {
322 | // Only set the timer if we have a timeout of at least 1 millisecond
323 | if (fadeOutTime > 0) {
324 | // Check if we need to create a new timer
325 | if (timer == null || timer._run == false) {
326 | // Create and start a new timer
327 | timer = new FadeTimer();
328 | timer.execute();
329 | } else {
330 | // Reset the current tiemr to 0
331 | timer.resetTimer();
332 | }
333 | }
334 | }
335 |
336 | /**
337 | * Counts from 0 to the fade out time and animates the view away when
338 | * reached
339 | */
340 | private class FadeTimer extends AsyncTask {
341 | // The current count
342 | private int timer = 0;
343 | // If we are inside the timing loop
344 | private boolean _run = true;
345 |
346 | public void resetTimer() {
347 | timer = 0;
348 | }
349 |
350 | @Override
351 | protected Void doInBackground(Void... arg0) {
352 | while (_run) {
353 | try {
354 | // Wait for a millisecond
355 | Thread.sleep(1);
356 | // Increment the timer
357 | timer++;
358 |
359 | // Check if we've reached the fade out time
360 | if (timer == fadeOutTime) {
361 | // Stop running
362 | _run = false;
363 | }
364 | } catch (InterruptedException e) {
365 | // TODO Auto-generated catch block
366 | e.printStackTrace();
367 | }
368 | }
369 | return null;
370 | }
371 |
372 | @Override
373 | protected void onPostExecute(Void result) {
374 | animation = AnimationUtils.loadAnimation(getContext(),
375 | android.R.anim.fade_out);
376 | animation.setAnimationListener(animationListener);
377 | startAnimation(animation);
378 | }
379 | }
380 |
381 | @Override
382 | public void onAnimationEnd(Animation animation) {
383 | setVisibility(View.GONE);
384 | }
385 |
386 | @Override
387 | public void onAnimationRepeat(Animation animation) {
388 | }
389 |
390 | @Override
391 | public void onAnimationStart(Animation animation) {
392 | }
393 | }
--------------------------------------------------------------------------------
/src/com/finddreams/bannerview/FlowIndicator.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2011 Patrik �kerfeldt
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.finddreams.bannerview;
17 |
18 | import com.finddreams.bannerview.ViewFlow.ViewSwitchListener;
19 |
20 | /**
21 | * An interface which defines the contract between a ViewFlow and a
22 | * FlowIndicator.
23 | * A FlowIndicator is responsible to show an visual indicator on the total views
24 | * number and the current visible view.
25 | * @author http://blog.csdn.net/finddreams
26 | */
27 | public interface FlowIndicator extends ViewSwitchListener {
28 |
29 | /**
30 | * Set the current ViewFlow. This method is called by the ViewFlow when the
31 | * FlowIndicator is attached to it.
32 | *
33 | * @param view
34 | */
35 | public void setViewFlow(ViewFlow view);
36 |
37 | /**
38 | * The scroll position has been changed. A FlowIndicator may implement this
39 | * method to reflect the current position
40 | *
41 | * @param h
42 | * @param v
43 | * @param oldh
44 | * @param oldv
45 | */
46 | public void onScrolled(int h, int v, int oldh, int oldv);
47 | }
48 |
--------------------------------------------------------------------------------
/src/com/finddreams/bannerview/ViewFlow.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2011 Patrik Åkerfeldt
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.finddreams.bannerview;
17 |
18 | import java.util.ArrayList;
19 | import java.util.LinkedList;
20 |
21 | import android.content.Context;
22 | import android.content.res.Configuration;
23 | import android.content.res.TypedArray;
24 | import android.database.DataSetObserver;
25 | import android.os.Handler;
26 | import android.os.Message;
27 | import android.util.AttributeSet;
28 | import android.view.MotionEvent;
29 | import android.view.VelocityTracker;
30 | import android.view.View;
31 | import android.view.ViewConfiguration;
32 | import android.view.ViewGroup;
33 | import android.view.ViewTreeObserver.OnGlobalLayoutListener;
34 | import android.widget.AbsListView;
35 | import android.widget.Adapter;
36 | import android.widget.AdapterView;
37 | import android.widget.Scroller;
38 |
39 | import com.finddreams.adbanner.R;
40 |
41 | /**
42 | *
43 | * A horizontally scrollable {@link ViewGroup} with items populated from an
44 | * {@link Adapter}. The ViewFlow uses a buffer to store loaded {@link View}s in.
45 | * The default size of the buffer is 3 elements on both sides of the currently
46 | * visible {@link View}, making up a total buffer size of 3 * 2 + 1 = 7. The
47 | * buffer size can be changed using the {@code sidebuffer} xml attribute.
48 | *
49 | * @author http://blog.csdn.net/finddreams
50 | */
51 | public class ViewFlow extends AdapterView {
52 |
53 | private static final int SNAP_VELOCITY = 1000;
54 | private static final int INVALID_SCREEN = -1;
55 | private final static int TOUCH_STATE_REST = 0;
56 | private final static int TOUCH_STATE_SCROLLING = 1;
57 |
58 | private LinkedList mLoadedViews;
59 | private int mCurrentBufferIndex;
60 | private int mCurrentAdapterIndex;
61 | private int mSideBuffer = 2;
62 | private Scroller mScroller;
63 | private VelocityTracker mVelocityTracker;
64 | private int mTouchState = TOUCH_STATE_REST;
65 | private float mLastMotionX;
66 | private int mTouchSlop;
67 | private int mMaximumVelocity;
68 | private int mCurrentScreen;
69 | private int mNextScreen = INVALID_SCREEN;
70 | private boolean mFirstLayout = true;
71 | private ViewSwitchListener mViewSwitchListener;
72 | private Adapter mAdapter;
73 | private int mLastScrollDirection;
74 | private AdapterDataSetObserver mDataSetObserver;
75 | private FlowIndicator mIndicator;
76 | private int mLastOrientation = -1;
77 | private long timeSpan = 3000;
78 | private Handler handler;
79 | private OnGlobalLayoutListener orientationChangeListener = new OnGlobalLayoutListener() {
80 |
81 | @Override
82 | public void onGlobalLayout() {
83 | getViewTreeObserver().removeGlobalOnLayoutListener(
84 | orientationChangeListener);
85 | setSelection(mCurrentAdapterIndex);
86 | }
87 | };
88 |
89 | /**
90 | * Receives call backs when a new {@link View} has been scrolled to.
91 | */
92 | public static interface ViewSwitchListener {
93 |
94 | /**
95 | * This method is called when a new View has been scrolled to.
96 | *
97 | * @param view
98 | * the {@link View} currently in focus.
99 | * @param position
100 | * The position in the adapter of the {@link View} currently in focus.
101 | */
102 | void onSwitched(View view, int position);
103 |
104 | }
105 |
106 | public ViewFlow(Context context) {
107 | super(context);
108 | mSideBuffer = 3;
109 | init();
110 | }
111 |
112 | public ViewFlow(Context context, int sideBuffer) {
113 | super(context);
114 | mSideBuffer = sideBuffer;
115 | init();
116 | }
117 |
118 | public ViewFlow(Context context, AttributeSet attrs) {
119 | super(context, attrs);
120 | TypedArray styledAttrs = context.obtainStyledAttributes(attrs,
121 | R.styleable.ViewFlow);
122 | mSideBuffer = styledAttrs.getInt(R.styleable.ViewFlow_sidebuffer, 3);
123 | init();
124 | }
125 |
126 | private void init() {
127 | mLoadedViews = new LinkedList();
128 | mScroller = new Scroller(getContext());
129 | final ViewConfiguration configuration = ViewConfiguration
130 | .get(getContext());
131 | mTouchSlop = configuration.getScaledTouchSlop();
132 | mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
133 | }
134 |
135 | public void startAutoFlowTimer(){
136 | handler = new Handler(){
137 | @Override
138 | public void handleMessage(Message msg) {
139 |
140 | snapToScreen((mCurrentScreen+1)%getChildCount());
141 | Message message = handler.obtainMessage(0);
142 | sendMessageDelayed(message, timeSpan);
143 | }
144 | };
145 |
146 | Message message = handler.obtainMessage(0);
147 | handler.sendMessageDelayed(message, timeSpan);
148 | }
149 | public void stopAutoFlowTimer(){
150 | if(handler!=null)
151 | handler.removeMessages(0);
152 | handler = null;
153 | }
154 |
155 | public void onConfigurationChanged(Configuration newConfig) {
156 | if (newConfig.orientation != mLastOrientation) {
157 | mLastOrientation = newConfig.orientation;
158 | getViewTreeObserver().addOnGlobalLayoutListener(orientationChangeListener);
159 | }
160 | }
161 |
162 | public int getViewsCount() {
163 | return mSideBuffer;
164 | }
165 |
166 | @Override
167 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
168 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
169 |
170 | final int width = MeasureSpec.getSize(widthMeasureSpec);
171 | final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
172 | if (widthMode != MeasureSpec.EXACTLY && !isInEditMode()) {
173 | throw new IllegalStateException(
174 | "ViewFlow can only be used in EXACTLY mode.");
175 | }
176 |
177 | final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
178 | if (heightMode != MeasureSpec.EXACTLY && !isInEditMode()) {
179 | throw new IllegalStateException(
180 | "ViewFlow can only be used in EXACTLY mode.");
181 | }
182 |
183 | // The children are given the same width and height as the workspace
184 | final int count = getChildCount();
185 | for (int i = 0; i < count; i++) {
186 | getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);
187 | }
188 |
189 | if (mFirstLayout) {
190 | mScroller.startScroll(0, 0, mCurrentScreen * width, 0, 0);
191 | mFirstLayout = false;
192 | }
193 | }
194 |
195 | @Override
196 | protected void onLayout(boolean changed, int l, int t, int r, int b) {
197 | int childLeft = 0;
198 |
199 | final int count = getChildCount();
200 | for (int i = 0; i < count; i++) {
201 | final View child = getChildAt(i);
202 | if (child.getVisibility() != View.GONE) {
203 | final int childWidth = child.getMeasuredWidth();
204 | child.layout(childLeft, 0, childLeft + childWidth,
205 | child.getMeasuredHeight());
206 | childLeft += childWidth;
207 | }
208 | }
209 | }
210 |
211 | @Override
212 | public boolean onInterceptTouchEvent(MotionEvent ev) {
213 | if (getChildCount() == 0)
214 | return false;
215 |
216 | if (mVelocityTracker == null) {
217 | mVelocityTracker = VelocityTracker.obtain();
218 | }
219 | mVelocityTracker.addMovement(ev);
220 |
221 | final int action = ev.getAction();
222 | final float x = ev.getX();
223 |
224 | switch (action) {
225 | case MotionEvent.ACTION_DOWN:
226 | /*
227 | * If being flinged and user touches, stop the fling. isFinished
228 | * will be false if being flinged.
229 | */
230 | if (!mScroller.isFinished()) {
231 | mScroller.abortAnimation();
232 | }
233 |
234 | // Remember where the motion event started
235 | mLastMotionX = x;
236 |
237 | mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST
238 | : TOUCH_STATE_SCROLLING;
239 | if(handler!=null)
240 | handler.removeMessages(0);
241 | break;
242 |
243 | case MotionEvent.ACTION_MOVE:
244 | final int xDiff = (int) Math.abs(x - mLastMotionX);
245 |
246 | boolean xMoved = xDiff > mTouchSlop;
247 |
248 | if (xMoved) {
249 | // Scroll if the user moved far enough along the X axis
250 | mTouchState = TOUCH_STATE_SCROLLING;
251 | }
252 |
253 | if (mTouchState == TOUCH_STATE_SCROLLING) {
254 | // Scroll to follow the motion event
255 | final int deltaX = (int) (mLastMotionX - x);
256 | mLastMotionX = x;
257 |
258 | final int scrollX = getScrollX();
259 | if (deltaX < 0) {
260 | if (scrollX > 0) {
261 | scrollBy(Math.max(-scrollX, deltaX), 0);
262 | }
263 | } else if (deltaX > 0) {
264 | final int availableToScroll = getChildAt(
265 | getChildCount() - 1).getRight()
266 | - scrollX - getWidth();
267 | if (availableToScroll > 0) {
268 | scrollBy(Math.min(availableToScroll, deltaX), 0);
269 | }
270 | }
271 | return true;
272 | }
273 | break;
274 |
275 | case MotionEvent.ACTION_UP:
276 | if (mTouchState == TOUCH_STATE_SCROLLING) {
277 | final VelocityTracker velocityTracker = mVelocityTracker;
278 | velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
279 | int velocityX = (int) velocityTracker.getXVelocity();
280 |
281 | if (velocityX > SNAP_VELOCITY && mCurrentScreen > 0) {
282 | // Fling hard enough to move left
283 | snapToScreen(mCurrentScreen - 1);
284 | } else if (velocityX < -SNAP_VELOCITY
285 | && mCurrentScreen < getChildCount() - 1) {
286 | // Fling hard enough to move right
287 | snapToScreen(mCurrentScreen + 1);
288 | } else {
289 | snapToDestination();
290 | }
291 |
292 | if (mVelocityTracker != null) {
293 | mVelocityTracker.recycle();
294 | mVelocityTracker = null;
295 | }
296 | }
297 |
298 | mTouchState = TOUCH_STATE_REST;
299 | if(handler!=null){
300 | Message message = handler.obtainMessage(0);
301 | handler.sendMessageDelayed(message, timeSpan);
302 | }
303 | break;
304 | case MotionEvent.ACTION_CANCEL:
305 | mTouchState = TOUCH_STATE_REST;
306 | }
307 | return false;
308 | }
309 |
310 | @Override
311 | public boolean onTouchEvent(MotionEvent ev) {
312 | if (getChildCount() == 0)
313 | return false;
314 |
315 | if (mVelocityTracker == null) {
316 | mVelocityTracker = VelocityTracker.obtain();
317 | }
318 | mVelocityTracker.addMovement(ev);
319 |
320 | final int action = ev.getAction();
321 | final float x = ev.getX();
322 |
323 | switch (action) {
324 | case MotionEvent.ACTION_DOWN:
325 | /*
326 | * If being flinged and user touches, stop the fling. isFinished
327 | * will be false if being flinged.
328 | */
329 | if (!mScroller.isFinished()) {
330 | mScroller.abortAnimation();
331 | }
332 |
333 | // Remember where the motion event started
334 | mLastMotionX = x;
335 |
336 | mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST
337 | : TOUCH_STATE_SCROLLING;
338 | if(handler!=null)
339 | handler.removeMessages(0);
340 | break;
341 |
342 | case MotionEvent.ACTION_MOVE:
343 | final int xDiff = (int) Math.abs(x - mLastMotionX);
344 |
345 | boolean xMoved = xDiff > mTouchSlop;
346 |
347 | if (xMoved) {
348 | // Scroll if the user moved far enough along the X axis
349 | mTouchState = TOUCH_STATE_SCROLLING;
350 | }
351 |
352 | if (mTouchState == TOUCH_STATE_SCROLLING) {
353 | // Scroll to follow the motion event
354 | final int deltaX = (int) (mLastMotionX - x);
355 | mLastMotionX = x;
356 |
357 | final int scrollX = getScrollX();
358 | if (deltaX < 0) {
359 | if (scrollX > 0) {
360 | scrollBy(Math.max(-scrollX, deltaX), 0);
361 | }
362 | } else if (deltaX > 0) {
363 | final int availableToScroll = getChildAt(
364 | getChildCount() - 1).getRight()
365 | - scrollX - getWidth();
366 | if (availableToScroll > 0) {
367 | scrollBy(Math.min(availableToScroll, deltaX), 0);
368 | }
369 | }
370 | return true;
371 | }
372 | break;
373 |
374 | case MotionEvent.ACTION_UP:
375 | if (mTouchState == TOUCH_STATE_SCROLLING) {
376 | final VelocityTracker velocityTracker = mVelocityTracker;
377 | velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
378 | int velocityX = (int) velocityTracker.getXVelocity();
379 |
380 | if (velocityX > SNAP_VELOCITY && mCurrentScreen > 0) {
381 | // Fling hard enough to move left
382 | snapToScreen(mCurrentScreen - 1);
383 | } else if (velocityX < -SNAP_VELOCITY
384 | && mCurrentScreen < getChildCount() - 1) {
385 | // Fling hard enough to move right
386 | snapToScreen(mCurrentScreen + 1);
387 | }
388 | // else if (velocityX < -SNAP_VELOCITY
389 | // && mCurrentScreen == getChildCount() - 1) {
390 | // snapToScreen(0);
391 | // }
392 | // else if (velocityX > SNAP_VELOCITY
393 | // && mCurrentScreen == 0) {
394 | // snapToScreen(getChildCount() - 1);
395 | // }
396 | else {
397 | snapToDestination();
398 | }
399 |
400 | if (mVelocityTracker != null) {
401 | mVelocityTracker.recycle();
402 | mVelocityTracker = null;
403 | }
404 | }
405 |
406 | mTouchState = TOUCH_STATE_REST;
407 |
408 | if(handler!=null){
409 | Message message = handler.obtainMessage(0);
410 | handler.sendMessageDelayed(message, timeSpan);
411 | }
412 | break;
413 | case MotionEvent.ACTION_CANCEL:
414 | snapToDestination();
415 | mTouchState = TOUCH_STATE_REST;
416 | }
417 | return true;
418 | }
419 |
420 | @Override
421 | protected void onScrollChanged(int h, int v, int oldh, int oldv) {
422 | super.onScrollChanged(h, v, oldh, oldv);
423 | if (mIndicator != null) {
424 | /*
425 | * The actual horizontal scroll origin does typically not match the
426 | * perceived one. Therefore, we need to calculate the perceived
427 | * horizontal scroll origin here, since we use a view buffer.
428 | */
429 | int hPerceived = h + (mCurrentAdapterIndex - mCurrentBufferIndex)
430 | * getWidth();
431 | mIndicator.onScrolled(hPerceived, v, oldh, oldv);
432 | }
433 | }
434 |
435 | private void snapToDestination() {
436 | final int screenWidth = getWidth();
437 | final int whichScreen = (getScrollX() + (screenWidth / 2))
438 | / screenWidth;
439 |
440 | snapToScreen(whichScreen);
441 | }
442 |
443 | private void snapToScreen(int whichScreen) {
444 | mLastScrollDirection = whichScreen - mCurrentScreen;
445 | if (!mScroller.isFinished())
446 | return;
447 |
448 | whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1));
449 |
450 | mNextScreen = whichScreen;
451 |
452 | final int newX = whichScreen * getWidth();
453 | final int delta = newX - getScrollX();
454 | mScroller.startScroll(getScrollX(), 0, delta, 0, Math.abs(delta) * 2);
455 | invalidate();
456 | }
457 |
458 | @Override
459 | public void computeScroll() {
460 | if (mScroller.computeScrollOffset()) {
461 | scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
462 | postInvalidate();
463 | } else if (mNextScreen != INVALID_SCREEN) {
464 | mCurrentScreen = Math.max(0,
465 | Math.min(mNextScreen, getChildCount() - 1));
466 | mNextScreen = INVALID_SCREEN;
467 | postViewSwitched(mLastScrollDirection);
468 | }
469 | }
470 |
471 | /**
472 | * Scroll to the {@link View} in the view buffer specified by the index.
473 | *
474 | * @param indexInBuffer
475 | * Index of the view in the view buffer.
476 | */
477 | private void setVisibleView(int indexInBuffer, boolean uiThread) {
478 | mCurrentScreen = Math.max(0,
479 | Math.min(indexInBuffer, getChildCount() - 1));
480 | int dx = (mCurrentScreen * getWidth()) - mScroller.getCurrX();
481 | mScroller.startScroll(mScroller.getCurrX(), mScroller.getCurrY(), dx,
482 | 0, 0);
483 | if(dx == 0)
484 | onScrollChanged(mScroller.getCurrX() + dx, mScroller.getCurrY(), mScroller.getCurrX() + dx, mScroller.getCurrY());
485 | if (uiThread)
486 | invalidate();
487 | else
488 | postInvalidate();
489 | }
490 |
491 | /**
492 | * Set the listener that will receive notifications every time the {code
493 | * ViewFlow} scrolls.
494 | *
495 | * @param l
496 | * the scroll listener
497 | */
498 | public void setOnViewSwitchListener(ViewSwitchListener l) {
499 | mViewSwitchListener = l;
500 | }
501 |
502 | @Override
503 | public Adapter getAdapter() {
504 | return mAdapter;
505 | }
506 |
507 | @Override
508 | public void setAdapter(Adapter adapter) {
509 | setAdapter(adapter, 0);
510 | }
511 |
512 | public void setAdapter(Adapter adapter, int initialPosition) {
513 | if (mAdapter != null) {
514 | mAdapter.unregisterDataSetObserver(mDataSetObserver);
515 | }
516 |
517 | mAdapter = adapter;
518 |
519 | if (mAdapter != null) {
520 | mDataSetObserver = new AdapterDataSetObserver();
521 | mAdapter.registerDataSetObserver(mDataSetObserver);
522 |
523 | }
524 | if (mAdapter == null || mAdapter.getCount() == 0)
525 | return;
526 |
527 | setSelection(initialPosition);
528 | }
529 |
530 | @Override
531 | public View getSelectedView() {
532 | return (mCurrentBufferIndex < mLoadedViews.size() ? mLoadedViews
533 | .get(mCurrentBufferIndex) : null);
534 | }
535 |
536 | @Override
537 | public int getSelectedItemPosition() {
538 | return mCurrentAdapterIndex;
539 | }
540 |
541 | /**
542 | * Set the FlowIndicator
543 | *
544 | * @param flowIndicator
545 | */
546 | public void setFlowIndicator(FlowIndicator flowIndicator) {
547 | mIndicator = flowIndicator;
548 | mIndicator.setViewFlow(this);
549 | }
550 |
551 | @Override
552 | public void setSelection(int position) {
553 | mNextScreen = INVALID_SCREEN;
554 | mScroller.forceFinished(true);
555 | if (mAdapter == null)
556 | return;
557 |
558 | position = Math.max(position, 0);
559 | position = Math.min(position, mAdapter.getCount()-1);
560 |
561 | ArrayList recycleViews = new ArrayList();
562 | View recycleView;
563 | while (!mLoadedViews.isEmpty()) {
564 | recycleViews.add(recycleView = mLoadedViews.remove());
565 | detachViewFromParent(recycleView);
566 | }
567 |
568 | View currentView = makeAndAddView(position, true,
569 | (recycleViews.isEmpty() ? null : recycleViews.remove(0)));
570 | mLoadedViews.addLast(currentView);
571 |
572 | for(int offset = 1; mSideBuffer - offset >= 0; offset++) {
573 | int leftIndex = position - offset;
574 | int rightIndex = position + offset;
575 | if(leftIndex >= 0)
576 | mLoadedViews.addFirst(makeAndAddView(leftIndex, false,
577 | (recycleViews.isEmpty() ? null : recycleViews.remove(0))));
578 | if(rightIndex < mAdapter.getCount())
579 | mLoadedViews.addLast(makeAndAddView(rightIndex, true,
580 | (recycleViews.isEmpty() ? null : recycleViews.remove(0))));
581 | }
582 |
583 | mCurrentBufferIndex = mLoadedViews.indexOf(currentView);
584 | mCurrentAdapterIndex = position;
585 |
586 | for (View view : recycleViews) {
587 | removeDetachedView(view, false);
588 | }
589 | requestLayout();
590 | setVisibleView(mCurrentBufferIndex, false);
591 | if (mIndicator != null) {
592 | mIndicator.onSwitched(mLoadedViews.get(mCurrentBufferIndex),
593 | mCurrentAdapterIndex);
594 | }
595 | if (mViewSwitchListener != null) {
596 | mViewSwitchListener
597 | .onSwitched(mLoadedViews.get(mCurrentBufferIndex),
598 | mCurrentAdapterIndex);
599 | }
600 | }
601 |
602 | private void resetFocus() {
603 | mLoadedViews.clear();
604 | removeAllViewsInLayout();
605 |
606 | for (int i = Math.max(0, mCurrentAdapterIndex - mSideBuffer); i < Math
607 | .min(mAdapter.getCount(), mCurrentAdapterIndex + mSideBuffer
608 | + 1); i++) {
609 | mLoadedViews.addLast(makeAndAddView(i, true, null));
610 | if (i == mCurrentAdapterIndex)
611 | mCurrentBufferIndex = mLoadedViews.size() - 1;
612 | }
613 | requestLayout();
614 | }
615 |
616 | private void postViewSwitched(int direction) {
617 | if (direction == 0)
618 | return;
619 |
620 | if (direction > 0) { // to the right
621 | mCurrentAdapterIndex++;
622 | mCurrentBufferIndex++;
623 |
624 | // if(direction > 1) {
625 | // mCurrentAdapterIndex += mAdapter.getCount() - 2;
626 | // mCurrentBufferIndex += mAdapter.getCount() - 2;
627 | // }
628 |
629 | View recycleView = null;
630 |
631 | // Remove view outside buffer range
632 | if (mCurrentAdapterIndex > mSideBuffer) {
633 | recycleView = mLoadedViews.removeFirst();
634 | detachViewFromParent(recycleView);
635 | // removeView(recycleView);
636 | mCurrentBufferIndex--;
637 | }
638 |
639 | // Add new view to buffer
640 | int newBufferIndex = mCurrentAdapterIndex + mSideBuffer;
641 | if (newBufferIndex < mAdapter.getCount())
642 | mLoadedViews.addLast(makeAndAddView(newBufferIndex, true,
643 | recycleView));
644 |
645 | } else { // to the left
646 | mCurrentAdapterIndex--;
647 | mCurrentBufferIndex--;
648 |
649 | // if(direction < -1) {
650 | // mCurrentAdapterIndex -= mAdapter.getCount() - 2;
651 | // mCurrentBufferIndex -= mAdapter.getCount() - 2;
652 | // }
653 |
654 | View recycleView = null;
655 |
656 | // Remove view outside buffer range
657 | if (mAdapter.getCount() - 1 - mCurrentAdapterIndex > mSideBuffer) {
658 | recycleView = mLoadedViews.removeLast();
659 | detachViewFromParent(recycleView);
660 | }
661 |
662 | // Add new view to buffer
663 | int newBufferIndex = mCurrentAdapterIndex - mSideBuffer;
664 | if (newBufferIndex > -1) {
665 | mLoadedViews.addFirst(makeAndAddView(newBufferIndex, false,
666 | recycleView));
667 | mCurrentBufferIndex++;
668 | }
669 |
670 | }
671 |
672 | requestLayout();
673 | setVisibleView(mCurrentBufferIndex, true);
674 | if (mIndicator != null) {
675 | mIndicator.onSwitched(mLoadedViews.get(mCurrentBufferIndex),
676 | mCurrentAdapterIndex);
677 | }
678 | if (mViewSwitchListener != null) {
679 | mViewSwitchListener
680 | .onSwitched(mLoadedViews.get(mCurrentBufferIndex),
681 | mCurrentAdapterIndex);
682 | }
683 | }
684 |
685 | private View setupChild(View child, boolean addToEnd, boolean recycle) {
686 | ViewGroup.LayoutParams p = (ViewGroup.LayoutParams) child
687 | .getLayoutParams();
688 | if (p == null) {
689 | p = new AbsListView.LayoutParams(
690 | ViewGroup.LayoutParams.FILL_PARENT,
691 | ViewGroup.LayoutParams.WRAP_CONTENT, 0);
692 | }
693 | if (recycle)
694 | attachViewToParent(child, (addToEnd ? -1 : 0), p);
695 | else
696 | addViewInLayout(child, (addToEnd ? -1 : 0), p, true);
697 | return child;
698 | }
699 |
700 | private View makeAndAddView(int position, boolean addToEnd, View convertView) {
701 | View view = mAdapter.getView(position, convertView, this);
702 | return setupChild(view, addToEnd, convertView != null);
703 | }
704 |
705 | class AdapterDataSetObserver extends DataSetObserver {
706 |
707 | @Override
708 | public void onChanged() {
709 | View v = getChildAt(mCurrentBufferIndex);
710 | if (v != null) {
711 | for (int index = 0; index < mAdapter.getCount(); index++) {
712 | if (v.equals(mAdapter.getItem(index))) {
713 | mCurrentAdapterIndex = index;
714 | break;
715 | }
716 | }
717 | }
718 | resetFocus();
719 | }
720 |
721 | @Override
722 | public void onInvalidated() {
723 | // Not yet implemented!
724 | }
725 |
726 | }
727 |
728 | public void setTimeSpan(long timeSpan) {
729 | this.timeSpan = timeSpan;
730 | }
731 |
732 | public void setmSideBuffer(int mSideBuffer) {
733 | this.mSideBuffer = mSideBuffer;
734 | }
735 | }
736 |
--------------------------------------------------------------------------------