1.功能分析
1.1 左右滑動切換圖片,并且實現循環切換。
1.2 自動切換圖片
1.3 導航圓點跟隨輪播變更
1.4 點擊圖片,實現監聽反饋
1.5 圖片需要適配屏幕,按定義寬高顯示
2.代碼實現
2.1 實現原理
每次加載顯示需要3張圖片,并且偏移至左中右三個位置,不斷地重繪view,修改偏移值,達到切換圖片效果。
2.2 代碼實現
創建自定義View類CarouselFigure,在onMeasure方法中,獲取容器view的寬度,這里使用默認顯示圖片寬度與view容器寬度比值,作為適配比,然后確定容器view顯示高度。
@Override
protected void onMeasure(intwidthMeasureSpec,intheightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);//獲取view寬高
mWidth = MeasureSpec.getSize(widthMeasureSpec);
mHeight = MeasureSpec.getSize(heightMeasureSpec);//默認showindex對應圖片適配后寬高,做輪播圖整體寬高
if(imgList!=null&& imgList.size()>0){
Bitmap bitmap = imgList.get(showIndex);floatscale = (float)mWidth / bitmap.getWidth();
mHeight = (int) (scale * bitmap.getHeight());
}
setMeasuredDimension(mWidth,mHeight);
}
手指左右滑動圖片時候,獲取橫向手指偏移量,偏移量具有方向,向左為負,向右為正。向左偏移,左中右圖片動態偏移量分別為,-mWidth+offset ,offset,mWidth+offset。showIndex表示顯示圖片imgList中索引號,pre_show_index為左圖索引號,next_show_index為右圖索引號。通過變更偏移和索引號,切換顯示圖片。
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//判斷偏移之后在臨界值
float min_x = mWidth-Math.abs(offset);
//小于臨界值時,改變顯示圖片索引值
if(min_x<MIN_WIDTH_OFFSET_VALUE){
//左移動
if(offset<0 ){
showIndex ++;
}
if(offset>0 ){
showIndex--;
}
//回歸
reSetValue();
}
//初始化showIndex
initShowIndex();
//構畫左中右三圖
handleImgWnH(pre_show_index);
handleImgWnH(showIndex);
handleImgWnH(next_show_index);
canvas.drawBitmap(imgList.get(pre_show_index),-mWidth+offset,0,mPaint);
canvas.drawBitmap(imgList.get(showIndex),offset,0,mPaint);
canvas.drawBitmap(imgList.get(next_show_index),mWidth+offset,0,mPaint);
//構畫導航圓點
drawCycle(canvas);
}
一開始觸摸圖片,記錄當前觸摸坐標,標記為初始坐標startPoint,移動時獲取實時坐標,計算手勢滑動方向與水平方向夾角正切值,判斷是否是水平滑動,如果是橫向水平滑動,則記錄滑動偏移量,調用invalidate進行重繪
@Override
public boolean onTouchEvent(MotionEvent event) {
//正在刷新禁止觸摸
if(isRefreshing){return true;}
//正在自動輪播時,不能觸發
if(isAutoSliding||isGestureSliding){return true;}
int action = event.getAction();
switch (action){
case MotionEvent.ACTION_DOWN:
//關閉自動輪播
isAllowAutoSlid = false;
startPoint.set(event.getX(),event.getY());//記錄初始值
return true;
case MotionEvent.ACTION_MOVE:
float min_x = Math.abs(startPoint.x - event.getX());
float min_y = Math.abs(startPoint.y - event.getY());
//獲取正切值
float angle_tan_value = min_y / min_x;
if(angle_tan_value<MIN_ANGLE_TAN_VALUE){ //橫向滑動
min_x = event.getX() - startPoint.x ;
offset = old_offset + min_x;
nowPoint.set(event.getX(),event.getY());//記錄當前坐標
invalidate();
}
return true;
case MotionEvent.ACTION_UP:
old_offset = offset;//記錄上一次偏移量
//滑動手勢產生圖片切換效果
post(slidImgRunnable);
//重新開啟自動輪播
isAllowAutoSlid = true;
if(offset==0) {
onTopImageClickListeners.onClick(showIndex);
}
return true;
}
return false;
}
此時,已經實現讓圖片跟隨手指偏移。但是圖片并不會自動保持滑動慣性,使自己顯示完全。 因此,需要在ACTION_UP處補充一個方法,讓圖片繼續完成剩下偏移。此處使用線程slidImgRunnable
private Runnable slidImgRunnable = new Runnable() {
@Override
public void run() {
float abs_offset = Math.abs(offset);
//向左移動
if(offset<0) {
if(abs_offset<ALLOW_SLID_IMG_OFFSET){ //允許產生滑動的偏移量,否則圖片回歸原位置
offset += SLID_IMG_INTERVAL_OFFSET;
if(offset>0){
reSetValue();
}
}else {
offset += -SLID_IMG_INTERVAL_OFFSET;
}
}else if(offset>0){ //向右移動
if(abs_offset<ALLOW_SLID_IMG_OFFSET){ //允許產生滑動的偏移量,否則圖片回歸原位置
offset += -SLID_IMG_INTERVAL_OFFSET;
if(offset<0){
reSetValue();
}
}else {
offset += SLID_IMG_INTERVAL_OFFSET;
}
}
//當滑動之后 offset重置為0 結束循環
if(abs_offset==0){
isGestureSliding = false;
return;
}
isGestureSliding = true;
invalidate();
postDelayed(slidImgRunnable,SLID_IMG_INTERVALS);
}
};
無論向左還是向右移動,offset 絕對值都在增大,并且offset區間在0~mWidth之間,所以,只要設定一個遞增偏移常量,不斷循環執行修改偏移量和重繪,就可以讓圖片自動完成偏移。
自動輪播效果,需要另啟一循環線程執行。autoSlidImg決定輪播周期,autoSlidImgRunnable 完成偏移量遞增,和重繪view
開啟自動輪播
/**
* 開啟自動輪播
*/
private void autoSlidImg() {
new Thread(new Runnable() {
@Override
public void run() {
try {
while (true){
Thread.sleep(4000);//每4秒輪播一次
if(isAllowAutoSlid){
post(autoSlidImgRunnable);
}
}
}catch (Exception e ){
}
}
}).start();
}
/**
* 實現自動輪播效果
*/
private Runnable autoSlidImgRunnable = new Runnable() {
@Override
public void run() {
offset += -SLID_IMG_INTERVAL_OFFSET;
if (isNextCycle) {//判斷是否可以繼續產生偏移,完成一次輪播后退出
offset=0;
isNextCycle=false;
isAutoSliding = false;
return;
}else{
isAutoSliding = true;//正在輪播滑動
}
invalidate();
postDelayed(autoSlidImgRunnable, SLID_IMG_INTERVALS);
}
};
定義接口返回點擊顯示圖片監聽
public interface OnTopImageClickListeners{
void onClick(int showIndex);
}
carouselFigure.setOnTopImageClickListeners(new CarouselFigure.OnTopImageClickListeners() {
@Override
public void onClick(int showIndex) {
Log.i("tag","index: "+showIndex);
}
});