大圖瀏覽可以說是所有App必備功能,可見其重要性,所以有必要將其獨立,便於維護和複用。本文程式碼基於SubsamplingScaleImageView開源庫實現,增加單手拖拽返回,透明度變化等效果。
瀏覽效果
由於平臺有圖片大小限制,壓縮後很模糊,請移步github檢視。
功能實現
超大圖瀏覽
SubsamplingScaleImageView基於BitmapRegionDecoder實現,避免OOM情況下,輕鬆瀏覽超大圖,支援各種手勢操作。感興趣的請查閱其原始碼。
瀏覽Activity背景透明
//manifest 設定Activity theme屬性 android:theme="@style/photoviewer_theme" //對應style定義 <style name="photoviewer_theme" parent="Theme.AppCompat.NoActionBar"> <item name="android:windowBackground">@android:color/transparent</item> <item name="android:windowIsTranslucent">true</item> </style>複製程式碼
沉浸式效果
private void hideSystemUI() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { View decorView = getWindow().getDecorView(); decorView.setSystemUiVisibility( View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_FULLSCREEN // hide status bar | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); } else { WindowManager.LayoutParams attrs = getWindow().getAttributes(); attrs.flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN; getWindow().setAttributes(attrs); getWindow().addFlags( WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS); } //change navigationbar bg color if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); getWindow().setNavigationBarColor(Color.TRANSPARENT); } }複製程式碼
自定義DragPhotoView攔截touch事件
setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { //如果正在動畫,攔截不處理。 if (isAnimating()) { return true; } //判斷圖片是否為初始狀態(第一次進入後顯示的大小) if (isReady() && (firstDisplayScale == getScale())) { final int action = event.getAction(); switch (action & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_DOWN: if (DEBUG) { Log.d(TAG, "action_down = " + firstDisplayScale + ", getScale = " + getScale() + " isFirstEnterState = " + (firstDisplayScale == getScale())); } downX = event.getX(); downY = event.getY(); canDrag = true; break; case MotionEvent.ACTION_MOVE: if (canDrag) { final float dy = Math.abs(downY - event.getY()); if (firstDisplayScale == getScale() && dy > touchSlop) { isDragging = true; translateX = event.getX() - downX; translateY = event.getY() - downY; float percent = dy / maxTranslateY; if (percent > 1.0f) { percent = 1.0f; } bgAlpha = (int) (255 * (1 - percent)); bgAlpha = bgAlpha < minBgAlpha ? minBgAlpha : bgAlpha; //儘量將圖片縮放比例設定小點 final float p = dy / getHeight(); bitmapScale = (1 - p); bitmapScale = bitmapScale < minBitmapScale ? minBitmapScale : bitmapScale; if(DEBUG) { Log.d(TAG, "action_move translateX = " + translateX + "; translateY = " + translateY + "; pointerCount = " + event.getPointerCount()); } invalidate(); } } break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: if(DEBUG) { Log.d(TAG, "action = " + action + "; pointerCount = " + event.getPointerCount()); } if (isDragging) { //最後一個手指離開螢幕時,檢查y軸方向拖拽距離是否超過閥值,若超過則回撥dismiss介面 if (Math.abs(translateY) >= maxTranslateY && dismissListener != null) { dismissListener.onDismiss(); }else{ //若為超過閥值,則指定動畫回到初始位置。 restoreFirstEnterState(); } isDragging = false; } break; case MotionEvent.ACTION_POINTER_UP: case MotionEvent.ACTION_POINTER_DOWN: if(DEBUG){ Log.d(TAG, "action pointer down or up= " + action + "; pointerCount = " + event.getPointerCount()); } //防止拖拽過程中多點觸控導致事件錯亂。 canDrag = isDragging; break; default: break; } } return isDragging; } }); @Override protected void onDraw(Canvas canvas) { //肯定touch move事件,不斷更新以下幾個變數的值來達到動畫效果。 createPaint(); bgPaint.setAlpha(bgAlpha); canvas.drawRect(0, 0, getWidth(), getHeight(), bgPaint); canvas.translate(translateX, translateY); canvas.scale(bitmapScale, bitmapScale, getWidth() / 2, getHeight() / 2); super.onDraw(canvas); }複製程式碼
點選位置(進入和退出動畫)
// TODO 非剛需,暫未實現。
原始碼地址
以上提及程式碼均在AndroidUiKit專案(安卓常用UI元件庫。 總結、沉澱、封裝優化;為避免重複造輪子,此專案會收集優秀的三方庫,或直接引用,或修改原始碼;目標很明確:快速整合開發,提高效率。)
程式碼位置:uikit module中photoviewer包下
App圖片處理庫推薦
- 圖片載入神器Glide
- Multi-media selector(本地圖片視訊選擇器) AndroidUiKit專案中有推薦。
本文屬原始碼內容,轉載請說明出處,首發部落格。