Android自定義View使用canvas實現輪播圖效果

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);
        }
});

github分享地址
CSDN下載地址

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,242評論 25 708
  • 主要思路 1.我們需要自定義一個繼承自FrameLayout的布局,利用FrameLayout布-局的特性(在同一...
    ZebraWei閱讀 2,332評論 0 5
  • 發現 關注 消息 iOS 第三方庫、插件、知名博客總結 作者大灰狼的小綿羊哥哥關注 2017.06.26 09:4...
    肇東周閱讀 12,229評論 4 61
  • 今天是朗軒的生日,晚上邀請了小伙伴一起玩。 昨天給他買了他一直朝朝暮暮的佩琦和喬治,因為從網上拍的,到貨后打開包裝...
    多金閱讀 181評論 0 0
  • 玉の緒のたえてみじかき命もて年月ながき戀もするかな
    松本靜閱讀 255評論 0 0