**(一):寫在前面的話**
接著上一篇繼續更新,上一篇文章已經把FastDev4Android項目做了大體的了解,包括項目結構已經需要進行完善的功能,那么今天我們繼續完善這個項目;今天我們主要將的是實現一個首頁自動無限循環組件我這邊采用的是Gallery(重寫)+FlowIndicator(自定義);?
[項目地址](https://github.com/jiangqqlmj/FastDev4Android "項目Github地址")
**(二)Gallery控件講解**
2.1:說明-實現效果如下:?
?
Gallery為畫廊控件相信大家對此非常熟悉,同時這個Gallery組件已經早就過時了,雖然官方說這個組件會造成內存過大,緩存機制不行,或者說緩存機制完全不行,不過如果作為初級階段要實現自動輪播用這個練手還是非常方便的。雖然現在一般可以采用viewpager或者RecyleView來進行實現,后面我們還會更新viewpager和recyleview實現的圖片輪播組件;?
2.2:實現方式:?
要實現圖片的自動無限輪播,那么最重要要實現自動輪播的功能,在Gallery我們只需要實現一個定時器,每個一段時間調用Gallery的方法來切換圖片如下:?
?
接著在定時器中直接調用onkeydown讓gallery來切換圖片,我們來看一下onkeydown的方法:
~~~
/**
* Handles left, right, and clicking
* @see android.view.View#onKeyDown
*/
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_DPAD_LEFT:
if (moveDirection(-1)) {
playSoundEffect(SoundEffectConstants.NAVIGATION_LEFT);
return true;
}
break;
case KeyEvent.KEYCODE_DPAD_RIGHT:
if (moveDirection(1)) {
playSoundEffect(SoundEffectConstants.NAVIGATION_RIGHT);
return true;
}
break;
case KeyEvent.KEYCODE_DPAD_CENTER:
case KeyEvent.KEYCODE_ENTER:
mReceivedInvokeKeyDown = true;
// fallthrough to default handling
}
return super.onKeyDown(keyCode, event);
}
~~~
上邊的源代碼我們可以很清晰看到KEYCODE_DPAD_LEFT,KEYCODE_DPAD_RIGHT這兩個參數,OK那么我們在調用onkeydown傳入參數的時候傳入這兩個值就OK了。那么我們該如果設置這兩個參數呢?其實很簡單gallery是提供我們手指滑動來切換的,也就是Gallery實現了GestureDetector.OnGestureListener手勢接口,那么我們可以重寫onFing方法判斷是向左還是向右滑動,代碼如下:
~~~
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
int kEvent;
if (isScrollingLeft(e1, e2)) {
kEvent = KeyEvent.KEYCODE_DPAD_LEFT; //設置手勢滑動的方法 --向左
} else {
kEvent = KeyEvent.KEYCODE_DPAD_RIGHT; //設置手勢滑動的方法--向右
}
onKeyDown(kEvent, null); //進行設置galler切換圖片
if (this.getSelectedItemPosition() == 0) {
this.setSelection(length);
}
return false;
}
~~~
下面我們來看一下AutoGallery的源代碼,詳細了解一下實現方法:
~~~
package com.chinaztt.fda.widget;
import android.content.Context;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Gallery;
import java.util.Timer;
import java.util.TimerTask;
/**
* 當前類注釋:重寫Gallery對畫廊控件進行重寫,擴展成可以自動切換圖片Gallery
* 這個圖片輪播控件還是比較之前封裝的,現在一般采用viewpager進行封裝,后面我這邊也會介紹
* 項目名:FastDev4Android
* 包名:com.chinaztt.fda.widget
* 作者:江清清 on 15/10/23 08:41
* 郵箱:jiangqqlmj@163.com
* QQ: 781931404
* 公司:江蘇中天科技軟件技術有限公司
*/
public class AutoGallery extends Gallery implements View.OnTouchListener {
//畫廊圖片的數量
private int length;
//自動切換圖片的時間
private long delayMillis = 5000;
//定時器
private Timer timer = null;
public AutoGallery(Context context) {
super(context);
setOnTouchListener(this);
}
public AutoGallery(Context context, AttributeSet attrs) {
super(context, attrs);
setOnTouchListener(this);
}
public AutoGallery(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setOnTouchListener(this);
}
public int getLength() {
return this.length;
}
public void setLength(int length) {
this.length = length;
}
public void setDelayMillis(long delayMillis) {
this.delayMillis = delayMillis;
}
/**
* 重寫Galler中手指滑動的手勢方法
* @param e1
* @param e2
* @param velocityX
* @param velocityY
* @return
*/
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
int kEvent;
if (isScrollingLeft(e1, e2)) {
kEvent = KeyEvent.KEYCODE_DPAD_LEFT; //設置手勢滑動的方法 --向左
} else {
kEvent = KeyEvent.KEYCODE_DPAD_RIGHT; //設置手勢滑動的方法--向右
}
onKeyDown(kEvent, null); //進行設置galler切換圖片
if (this.getSelectedItemPosition() == 0) {
this.setSelection(length);
}
return false;
}
/**
* 進行判斷滑動方向
* @param e1
* @param e2
* @return
*/
private boolean isScrollingLeft(MotionEvent e1, MotionEvent e2) {
return e2.getX() > e1.getX();
}
/**
* 開啟定時器
*/
public void start() {
if (length > 0&&timer == null) {
timer = new Timer();
//進行每個delayMillis時間gallery切換一張圖片
timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
if (length > 0) {
onKeyDown(KeyEvent.KEYCODE_DPAD_RIGHT, null);
}
}
}, delayMillis, delayMillis);
}
}
/**
* 關閉定時器
*/
public void stop() {
if (timer != null) {
timer.cancel();
timer = null;
}
}
/**
* 重寫手指觸摸的事件,當手指按下的時候,需要關閉gallery自動切換
* 當手指抬開得時候 需要打開gallery自動切換功能
* @param v
* @param event
* @return
*/
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
stop();
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
start();
break;
}
return false;
}
}
~~~
**(三)FlowIndicator控件講解**
3.1:實現效果:?

3.2:指示器是一個自定義的view,通過實時的繪制,首先我們需要定義圓點的attrs屬性文件如下:
~~~
<!--圖片輪播下面指示器自定義屬性-->
<declare-styleable name="FlowIndicator">
<attr name="point_normal_color" format="color" /> <!--指示器小圓點正常顏色-->
<attr name="point_seleted_color" format="color" /> <!--指示器小圓點選中顏色-->
<attr name="radius" format="dimension" /> <!--指示器原點的半徑-->
<attr name="count" format="integer" /> <!--指示器原點的數量-->
<attr name="space" format="dimension" /> <!--每個指示器原點的間隔-->
</declare-styleable>
~~~
然后我們在FlowDicator中進行獲取到相關屬性,并且進行繪制ondraw即可,不過在繪制的時候需要判斷以下當前是否選中,這樣分別繪制選中和未選中的原點;
~~~
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
float width = (getWidth() - ((radius * 2 * count) + (space * (count - 1)))) / 2.f;
Log.d(TAG_FLOWINDICATOR,"當前選中的為:"+this.seleted);
for (int i = 0; i < count; i++) {
if (i == seleted) {
paint.setStyle(Style.FILL);
canvas.drawBitmap(bmp_selected, 130+width + getPaddingLeft()
+ radius + i * (space + radius + radius), 0, null);
} else {
paint.setStyle(Style.FILL);
canvas.drawBitmap(bmp_normal, 130+width + getPaddingLeft() + radius
+ i * (space + radius + radius), 0, null);
}
}
}
~~~
~~~
3.3:具體實現代碼,FlowIndicator.java
~~~
~~~
package com.chinaztt.fda.widget;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.util.AttributeSet;
import android.view.View;
import com.chinaztt.fda.ui.R;
import com.chinaztt.fda.utils.Log;
/**
* 自動播放Gallery指示器
* @author jiangqq
*
*/
public class FlowIndicator extends View {
private static final String TAG_FLOWINDICATOR="FlowIndicator";
private int count;
private float space, radius;
private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
private Bitmap bmp_selected, bmp_normal;
// 選中
private int seleted = 0;
public FlowIndicator(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.FlowIndicator);
//小圓點數量
count = a.getInteger(R.styleable.FlowIndicator_count, 4);
//每個小圓點間隔距離
space = a.getDimension(R.styleable.FlowIndicator_space, 4);
//小圓點半徑
radius = a.getDimension(R.styleable.FlowIndicator_radius, 7);
//正常 沒有選中的圖片
bmp_normal = BitmapFactory.decodeResource(getResources(),
R.drawable.hui);
//選中的圖片
bmp_selected = BitmapFactory.decodeResource(getResources(),
R.drawable.lan);
a.recycle();
}
//當前選中的索引,并且重繪指示器view
public void setSeletion(int index) {
this.seleted = index;
invalidate();
}
//設置指示器的數量
public void setCount(int count) {
this.count = count;
invalidate();
}
//設置指示器 下一個圓點
public void next() {
if (seleted < count - 1)
seleted++;
else
seleted = 0;
invalidate();
}
//設置指示器 前一個圓點
public void previous() {
if (seleted > 0)
seleted--;
else
seleted = count - 1;
invalidate();
}
/**
* 重寫繪制指示器view
* @param canvas
*/
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
float width = (getWidth() - ((radius * 2 * count) + (space * (count - 1)))) / 2.f;
Log.d(TAG_FLOWINDICATOR,"當前選中的為:"+this.seleted);
for (int i = 0; i < count; i++) {
if (i == seleted) {
paint.setStyle(Style.FILL);
canvas.drawBitmap(bmp_selected, 130+width + getPaddingLeft()
+ radius + i * (space + radius + radius), 0, null);
} else {
paint.setStyle(Style.FILL);
canvas.drawBitmap(bmp_normal, 130+width + getPaddingLeft() + radius
+ i * (space + radius + radius), 0, null);
}
}
}
/**
* 進行view大小的測量
* @param widthMeasureSpec
* @param heightMeasureSpec
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(measureWidth(widthMeasureSpec),
measureHeight(heightMeasureSpec));
}
private int measureWidth(int measureSpec) {
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
if (specMode == MeasureSpec.EXACTLY) {
result = specSize;
} else {
result = (int) (getPaddingLeft() + getPaddingRight()
+ (count * 2 * radius) + (count - 1) * radius + 1);
if (specMode == MeasureSpec.AT_MOST) {
result = Math.min(result, specSize);
}
}
return result;
}
private int measureHeight(int measureSpec) {
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
if (specMode == MeasureSpec.EXACTLY) {
result = specSize;
} else {
result = (int) (2 * radius + getPaddingTop() + getPaddingBottom() + 1);
if (specMode == MeasureSpec.AT_MOST) {
result = Math.min(result, specSize);
}
}
return result;
}
}
~~~
**(四):該組件的使用方法**
我們創建布局文件使用當前組件的包名路徑,然后進行初始化并且設置數據,最后綁定事件監聽器即可;
~~~
package com.chinaztt.fda.test;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import com.chinaztt.fda.ui.R;
import com.chinaztt.fda.ui.base.BaseActivity;
import com.chinaztt.fda.widget.AutoGallery;
import com.chinaztt.fda.widget.FlowIndicator;
/**
* 當前類注釋: 圖片輪播封裝類的簡單使用
* 項目名:FastDev4Android
* 包名:com.chinaztt.fda.test
* 作者:江清清 on 15/10/23 08:35
* 郵箱:jiangqqlmj@163.com
* QQ: 781931404
* 公司:江蘇中天科技軟件技術有限公司
*/
public class GalleryIndicatorActivity extends BaseActivity{
private LayoutInflater mInflater;
private int[] mImages;
private AutoGallery headline_image_gallery; //自動圖片輪播Gallery
private FlowIndicator galleryFlowIndicator; //指示器控件
private int circleSelectedPosition = 0; // 默認指示器的圓圈的位置為第一項
private int gallerySelectedPositon = 0; // 默認gallery的圖片為第一張
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.gallery_indicator_layout);
mInflater=getLayouInflater();
mImages=new int[]{
R.drawable.one,
R.drawable.two,
R.drawable.three,
R.drawable.four
};
headline_image_gallery=(AutoGallery)this.findViewById(R.id.headline_image_gallery);
galleryFlowIndicator=(FlowIndicator)this.findViewById(R.id.headline_circle_indicator);
int topSize = mImages.length;
//設置指示器圓點的數量
galleryFlowIndicator.setCount(topSize);
//設置當前的位置
galleryFlowIndicator.setSeletion(circleSelectedPosition);
//設置畫廊 圖片的數量
headline_image_gallery.setLength(topSize);
headline_image_gallery.setAdapter(new GalleryIndicatorAdapter());
gallerySelectedPositon = topSize * 20 + circleSelectedPosition;
//設置畫廊當前所指的位置 索引
headline_image_gallery.setSelection(gallerySelectedPositon);
headline_image_gallery.start();
//gallery滾動選擇監聽
headline_image_gallery
.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent,
View view, int position, long id) {
gallerySelectedPositon = position;
circleSelectedPosition = position
% headline_image_gallery.getLength();
galleryFlowIndicator
.setSeletion(circleSelectedPosition);
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
//gallery點擊選中事件
headline_image_gallery.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
int index=position
% headline_image_gallery.getLength()+1;
showToastMsgShort("點擊了第"+index+"個圖片!");
}
});
}
class GalleryIndicatorAdapter extends BaseAdapter{
@Override
public int getCount() {
return Integer.MAX_VALUE;
}
@Override
public Object getItem(int position) {
return mImages[position];
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Hondler _Hondler=null;
if(convertView==null){
_Hondler=new Hondler();
convertView=mInflater.inflate(R.layout.headline_gallery_item,null);
_Hondler.headline_gallery_imageview=(ImageView)convertView.findViewById(R.id.headline_gallery_imageview);
convertView.setTag(_Hondler);
}else
{
_Hondler=(Hondler)convertView.getTag();
}
int mPosition = position % mImages.length;
_Hondler.headline_gallery_imageview.setImageResource(mImages[mPosition]);
return convertView;
}
}
static class Hondler{
ImageView headline_gallery_imageview;
}
}
~~~
上面是該組件的基本使用方法,由于篇幅的原因,布局文件這類我這邊就沒有貼上去,主要講解了控件的主要實現方法,如果需要具體了解該項目和該組件可以去github中clone一下代碼;?
項目地址如下:[https://github.com/jiangqqlmj/FastDev4Android](https://github.com/jiangqqlmj/FastDev4Android)
- 前言
- Android快速開發框架介紹(一)
- Android首頁圖片自動無限循環輪播Gallery+FlowIndicator(二)
- Android 列表下拉刷新組件PullToRefreshListView使用(三)
- Android 數據緩存器ACache的詳解和使用(四)
- Android崩潰異常捕捉CustomCrash,提升用戶體驗(五)
- Android實現沉浸式狀態欄(六)
- AndroidAnnnotations注入框架介紹和Android Studios基本配置(七)
- AndroidAnnnotations注入框架的工作原理(八)
- AndroidAnnnotations注入框架使用之注入組件Components(九)
- AndroidAnnnotations注入框架使用之Injection標簽詳解(十)
- AndroidAnnnotations注入框架使用之事件綁定Event Binding(十一)
- AndroidAnnnotations注入框架使用之線程處理Threading(十二)
- AndroidAnnnotations注入框架使用之第三方框架集成RoboGuice(十三)
- AndroidAnnnotations注入框架使用之第三方框架集成Otto事件總線(十四)
- AndroidAnnnotations注入框架使用之第三方框架集成OrmLite(十五)
- AndroidAnnnotations注入框架使用之最佳實踐之Adapters和lists(十六)
- AndroidAnnnotations注入框架使用之最佳實踐SharedPreferences(十七)
- Android MVP開發模式詳解(十九)
- 消息總線EventBus的基本使用(二十)
- 消息總線EventBus源碼分析以及與Otto框架對比(二十一)
- 列表頭生成帶文本或者字母的圖片開源庫TextDrawable使用和詳解(二十二)
- 重寫WebView網頁加載以及JavaScript注入詳解(二十三)
- BaseAdapterHelper的基本使用介紹,讓你擺脫狂寫一堆Adapter煩惱(二十四)
- BaseAdapterHelper詳解源碼分析,讓你擺脫狂寫一堆Adapter煩惱(二十五)
- Volley完全解析之基礎使用(二十六)
- Volley完全解析之進階最佳實踐與二次封裝(二十七)
- RecyclerView完全解析,讓你從此愛上它(二十八)
- RecyclerView完全解析之打造新版類Gallery效果(二十九)
- RecyclerView完全解析之結合AA(Android Annotations)注入框架實例(三十)
- RecyclerView完全解析之下拉刷新與上拉加載SwipeRefreshLayout(三十一)
- CardView完全解析與RecyclerView結合使用(三十二)
- 神器ViewDragHelper完全解析,媽媽再也不擔心我自定義ViewGroup滑動View操作啦~(三十三)
- 神器ViewDragHelper完全解析之詳解實現QQ5.X側滑酷炫效果(三十四)
- 實例解析之SwipeRefreshLayout+RecyclerView+CardView(三十五)
- HorizontalScrollView,Fragment,FragmentStatePagerAdapter打造網易新聞Tab及滑動頁面效果(三十六)
- Android Design支持庫TabLayout打造仿網易新聞Tab標簽效果(三十七)
- 打造QQ6.X最新版本側滑界面效果(三十八)