轉載請標明出處:
http://www.lxweimin.com/p/2e2e7b41bd0b
本文出自:【江清清博客-代號獨狼】
【FastDev4Android框架開發】Android首頁圖片自動無限循環輪播Gallery+FlowIndicator(二)
(一):寫在前面的話
接著上一篇繼續更新,上一篇文章已經把FastDev4Android項目做了大體的了解,包括項目結構已經需要進行完善的功能,那么今天我們繼續完善這個項目;今天我們主要將的是實現一個首頁自動無限循環組件我這邊采用的是Gallery(重寫)+FlowIndicator(自定義);
項目地址:https://github.com/jiangqqlmj/FastDev4Android
(二)Gallery控件講解
2.1:說明-實現效果如下:
Gallery為畫廊控件相信大家對此非常熟悉,同時這個Gallery組件已經早就過時了,雖然官方說這個組件會造成內存過大,緩存機制不行,或者說緩存機制完全不行,不過如果作為初級階段要實現自動輪播用這個練手還是非常方便的。雖然現在一般可以采用viewpager或者RecyleView來進行實現,后面我們還會更新viewpager和recyleview實現的圖片輪播組件;
2.2:實現方式:
要實現圖片的自動無限輪播,那么最重要要實現自動輪播的功能,在Gallery我們只需要實現一個定時器,每個一段時間調用Gallery的方法來切換圖片如下:
接著在定時器中直接調用onkeydown讓gallery來切換圖片,我們來看一下onkeydown的方法:
code-lang
/
Handles left, right, and clicking
@see android.view.View#onKeyDown
/
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODEDPADLEFT:
if (moveDirection(-1)) {
playSoundEffect(SoundEffectConstants.NAVIGATIONLEFT);
return true;
}
break;
case KeyEvent.KEYCODEDPADRIGHT:
if (moveDirection(1)) {
playSoundEffect(SoundEffectConstants.NAVIGATIONRIGHT);
return true;
}
break;
case KeyEvent.KEYCODEDPADCENTER:
case KeyEvent.KEYCODEENTER:
mReceivedInvokeKeyDown = true;
// fallthrough to default handling
}
return super.onKeyDown(keyCode, event);
}
上邊的源代碼我們可以很清晰看到KEYCODEDPADLEFT,KEYCODEDPADRIGHT這兩個參數,OK那么我們在調用onkeydown傳入參數的時候傳入這兩個值就OK了。那么我們該如果設置這兩個參數呢?其實很簡單gallery是提供我們手指滑動來切換的,也就是Gallery實現了GestureDetector.OnGestureListener手勢接口,那么我們可以重寫onFing方法判斷是向左還是向右滑動,代碼如下:
code-lang
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
int kEvent;
if (isScrollingLeft(e1, e2)) {
kEvent = KeyEvent.KEYCODEDPADLEFT; //設置手勢滑動的方法 --向左
} else {
kEvent = KeyEvent.KEYCODEDPADRIGHT; //設置手勢滑動的方法--向右
}
onKeyDown(kEvent, null); //進行設置galler切換圖片
if (this.getSelectedItemPosition() == 0) {
this.setSelection(length);
}
return false;
}
下面我們來看一下AutoGallery的源代碼,詳細了解一下實現方法:
code-lang
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.KEYCODEDPADLEFT; //設置手勢滑動的方法 --向左
} else {
kEvent = KeyEvent.KEYCODEDPADRIGHT; //設置手勢滑動的方法--向右
}
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.KEYCODEDPADRIGHT, 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.ACTIONDOWN:
stop();
break;
case MotionEvent.ACTIONUP:
case MotionEvent.ACTIONCANCEL:
start();
break;
}
return false;
}
}
(三)FlowIndicator控件講解
3.1:實現效果:
3.2:指示器是一個自定義的view,通過實時的繪制,首先我們需要定義圓點的attrs屬性文件如下:
code-lang
normalcolor" format="color">
seletedcolor" format="color">
然后我們在FlowDicator中進行獲取到相關屬性,并且進行繪制ondraw即可,不過在繪制的時候需要判斷以下當前是否選中,這樣分別繪制選中和未選中的原點;
code-lang
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
float width = (getWidth() - ((radius2count) + (space(count - 1)))) / 2.f;
Log.d(TAGFLOWINDICATOR,"當前選中的為:"+this.seleted);
for (int i = 0; i < count; i++) {
if (i == seleted) {
paint.setStyle(Style.FILL);
canvas.drawBitmap(bmpselected, 130+width + getPaddingLeft()
radius + i(space + radius + radius), 0, null);
} else {
paint.setStyle(Style.FILL);
canvas.drawBitmap(bmpnormal, 130+width + getPaddingLeft() + radius
i(space + radius + radius), 0, null);
}
}
}
3.3:具體實現代碼,FlowIndicator.java
code-lang
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 TAGFLOWINDICATOR="FlowIndicator";
private int count;
private float space, radius;
private Paint paint = new Paint(Paint.ANTIALIASFLAG);
private Bitmap bmpselected, bmpnormal;
// 選中
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.FlowIndicatorcount, 4);
//每個小圓點間隔距離
space = a.getDimension(R.styleable.FlowIndicatorspace, 4);
//小圓點半徑
radius = a.getDimension(R.styleable.FlowIndicatorradius, 7);
//正常 沒有選中的圖片
bmpnormal = BitmapFactory.decodeResource(getResources(),
R.drawable.hui);
//選中的圖片
bmpselected = 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() - ((radius2count) + (space(count - 1)))) / 2.f;
Log.d(TAGFLOWINDICATOR,"當前選中的為:"+this.seleted);
for (int i = 0; i < count; i++) {
if (i == seleted) {
paint.setStyle(Style.FILL);
canvas.drawBitmap(bmpselected, 130+width + getPaddingLeft()
radius + i(space + radius + radius), 0, null);
} else {
paint.setStyle(Style.FILL);
canvas.drawBitmap(bmpnormal, 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()
(count2radius) + (count - 1)radius + 1);
if (specMode == MeasureSpec.ATMOST) {
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) (2radius + getPaddingTop() + getPaddingBottom() + 1);
if (specMode == MeasureSpec.ATMOST) {
result = Math.min(result, specSize);
}
}
return result;
}
}
(四):該組件的使用方法
我們創建布局文件使用當前組件的包名路徑,然后進行初始化并且設置數據,最后綁定事件監聽器即可;
code-lang
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 intmImages;
private AutoGallery headlineimagegallery; //自動圖片輪播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.galleryindicatorlayout);
mInflater=getLayouInflater();
mImages=new int{
R.drawable.one,
R.drawable.two,
R.drawable.three,
R.drawable.four
};
headlineimagegallery=(AutoGallery)this.findViewById(R.id.headlineimagegallery);
galleryFlowIndicator=(FlowIndicator)this.findViewById(R.id.headlinecircleindicator);
int topSize = mImages.length;
//設置指示器圓點的數量
galleryFlowIndicator.setCount(topSize);
//設置當前的位置
galleryFlowIndicator.setSeletion(circleSelectedPosition);
//設置畫廊 圖片的數量
headlineimagegallery.setLength(topSize);
headlineimagegallery.setAdapter(new GalleryIndicatorAdapter());
gallerySelectedPositon = topSize20 + circleSelectedPosition;
//設置畫廊當前所指的位置 索引
headlineimagegallery.setSelection(gallerySelectedPositon);
headlineimagegallery.start();
//gallery滾動選擇監聽
headlineimagegallery
.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView parent,
View view, int position, long id) {
gallerySelectedPositon = position;
circleSelectedPosition = position
% headlineimagegallery.getLength();
galleryFlowIndicator
.setSeletion(circleSelectedPosition);
}
@Override
public void onNothingSelected(AdapterView parent) {
}
});
//gallery點擊選中事件
headlineimagegallery.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView parent, View view, int position, long id) {
int index=position
% headlineimagegallery.getLength()+1;
showToastMsgShort("點擊了第"+index+"個圖片!");
}
});
}
class GalleryIndicatorAdapter extends BaseAdapter{
@Override
public int getCount() {
return Integer.MAXVALUE;
}
@Override
public Object getItem(int position) {
return mImagesposition;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
HondlerHondler=null;
if(convertView==null){
Hondler=new Hondler();
convertView=mInflater.inflate(R.layout.headlinegalleryitem,null);
Hondler.headlinegalleryimageview=(ImageView)convertView.findViewById(R.id.headlinegalleryimageview);
convertView.setTag(Hondler);
}else
{
Hondler=(Hondler)convertView.getTag();
}
int mPosition = position % mImages.length;
Hondler.headlinegalleryimageview.setImageResource(mImagesmPosition);
return convertView;
}
}
static class Hondler{
ImageView headlinegalleryimageview;
}
}
上面是該組件的基本使用方法,由于篇幅的原因,布局文件這類我這邊就沒有貼上去,主要講解了控件的主要實現方法,如果需要具體了解該項目和該組件可以去github中clone一下代碼;