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(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//擷取view寬高
mWidth = MeasureSpec.getSize(widthMeasureSpec);
mHeight = MeasureSpec.getSize(heightMeasureSpec);
//預設showindex對應圖檔适配後寬高,做輪播圖整體寬高
if(imgList!=null && imgList.size()>){
Bitmap bitmap = imgList.get(showIndex);
float scale = (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< ){
showIndex ++;
}
if(offset> ){
showIndex--;
}
//回歸
reSetValue();
}
//初始化showIndex
initShowIndex();
//構畫左中右三圖
handleImgWnH(pre_show_index);
handleImgWnH(showIndex);
handleImgWnH(next_show_index);
canvas.drawBitmap(imgList.get(pre_show_index),-mWidth+offset,,mPaint);
canvas.drawBitmap(imgList.get(showIndex),offset,,mPaint);
canvas.drawBitmap(imgList.get(next_show_index),mWidth+offset,,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==) {
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<) {
if(abs_offset<ALLOW_SLID_IMG_OFFSET){ //允許産生滑動的偏移量,否則圖檔回歸原位置
offset += SLID_IMG_INTERVAL_OFFSET;
if(offset>){
reSetValue();
}
}else {
offset += -SLID_IMG_INTERVAL_OFFSET;
}
}else if(offset>){ //向右移動
if(abs_offset<ALLOW_SLID_IMG_OFFSET){ //允許産生滑動的偏移量,否則圖檔回歸原位置
offset += -SLID_IMG_INTERVAL_OFFSET;
if(offset<){
reSetValue();
}
}else {
offset += SLID_IMG_INTERVAL_OFFSET;
}
}
//當滑動之後 offset重置為0 結束循環
if(abs_offset==){
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();//每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=;
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);
}
});
github分享位址
CSDN下載下傳位址