189 | * Converts the cursor into a CharSequence. Subclasses should override this method to convert 190 | * their results. The default implementation returns an empty String for null values or the 191 | * default String representation of the value. 192 | *
193 | * 194 | * @param cursor the cursor to convert to a CharSequence 195 | * @return a CharSequence representing the value 196 | */ 197 | public CharSequence convertToString(Cursor cursor) { 198 | return cursor == null ? "" : cursor.toString(); 199 | } 200 | 201 | 202 | 203 | /** 204 | * Called when the {@link ContentObserver} on the cursor receives a change 205 | * notification. 206 | * The default implementation provides the auto-requery logic, but may be overridden by 207 | * sub classes. 208 | * 209 | * @see ContentObserver#onChange(boolean) 210 | */ 211 | protected void onContentChanged() { 212 | 213 | } 214 | 215 | private class ChangeObserver extends ContentObserver { 216 | public ChangeObserver() { 217 | super(new Handler()); 218 | } 219 | 220 | @Override 221 | public boolean deliverSelfNotifications() { 222 | return true; 223 | } 224 | 225 | @Override 226 | public void onChange(boolean selfChange) { 227 | onContentChanged(); 228 | } 229 | } 230 | 231 | private class CursorDataSetObserver extends DataSetObserver { 232 | @Override 233 | public void onChanged() { 234 | mDataValid = true; 235 | notifyDataSetChanged(); 236 | } 237 | 238 | @Override 239 | public void onInvalidated() { 240 | mDataValid = false; 241 | // There is no notifyDataSetInvalidated() method in RecyclerView.Adapter 242 | notifyDataSetChanged(); 243 | } 244 | } 245 | } 246 | -------------------------------------------------------------------------------- /imagepicker/src/main/java/com/imnjh/imagepicker/widget/CheckBox.java: -------------------------------------------------------------------------------- 1 | package com.imnjh.imagepicker.widget; 2 | 3 | import android.animation.ObjectAnimator; 4 | import android.content.Context; 5 | import android.content.res.TypedArray; 6 | import android.graphics.Bitmap; 7 | import android.graphics.Canvas; 8 | import android.graphics.Color; 9 | import android.graphics.Paint; 10 | import android.graphics.PorterDuff; 11 | import android.graphics.PorterDuffXfermode; 12 | import android.graphics.drawable.Drawable; 13 | import android.text.Layout; 14 | import android.text.StaticLayout; 15 | import android.text.TextPaint; 16 | import android.text.TextUtils; 17 | import android.util.AttributeSet; 18 | import android.view.View; 19 | 20 | import com.imnjh.imagepicker.R; 21 | import com.imnjh.imagepicker.util.SystemUtil; 22 | 23 | /** 24 | * Created by Martin on 2017/1/17. 25 | */ 26 | public class CheckBox extends View { 27 | 28 | private Drawable checkDrawable; 29 | private static Paint paint; 30 | private static Paint eraser; 31 | private static Paint eraser2; 32 | private static Paint backgroundPaint; 33 | private static TextPaint textPaint; 34 | 35 | private Bitmap drawBitmap; 36 | private Bitmap checkBitmap; 37 | private Canvas bitmapCanvas; 38 | private Canvas checkCanvas; 39 | 40 | private StaticLayout textLayout; 41 | private float textWidth; 42 | private float textHeight; 43 | private float textLeft; 44 | 45 | private boolean drawBackground; 46 | 47 | private float progress; 48 | private ObjectAnimator checkAnimator; 49 | private boolean isCheckAnimation = true; 50 | 51 | private boolean attachedToWindow; 52 | private boolean isChecked; 53 | 54 | private int size; 55 | private int checkOffset; 56 | private int normalColor; 57 | private int disabledColor; 58 | private int borderColor; 59 | private int unCheckedColor; 60 | private boolean unchecked_invisible; 61 | private boolean showBorderInCheckState; 62 | 63 | private final static float progressBounceDiff = 0.2f; 64 | 65 | public CheckBox(Context context) { 66 | this(context, null); 67 | } 68 | 69 | public CheckBox(Context context, AttributeSet attrs) { 70 | this(context, attrs, 0); 71 | } 72 | 73 | public CheckBox(Context context, AttributeSet attrs, int defStyleAttr) { 74 | super(context, attrs, defStyleAttr); 75 | TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CheckBox); 76 | normalColor = 77 | a.getColor(R.styleable.CheckBox_checkbox_color, 78 | getResources().getColor(R.color.color_48baf3)); 79 | unCheckedColor = 80 | a.getColor(R.styleable.CheckBox_unchecked_color, 81 | getResources().getColor(R.color.transparent_70_black)); 82 | disabledColor = 83 | a.getColor(R.styleable.CheckBox_disabled_color, 84 | getResources().getColor(R.color.color_e5e5e5)); 85 | borderColor = 86 | a.getColor(R.styleable.CheckBox_border_color, Color.WHITE); 87 | size = a.getDimensionPixelSize(R.styleable.CheckBox_checkbox_size, SystemUtil.dp(22)); 88 | unchecked_invisible = a.getBoolean(R.styleable.CheckBox_invisible_in_unchecked_state, false); 89 | checkDrawable = a.getDrawable(R.styleable.CheckBox_checked_drawable); 90 | showBorderInCheckState = a.getBoolean(R.styleable.CheckBox_show_border_in_check_state, false); 91 | a.recycle(); 92 | init(); 93 | } 94 | 95 | private void init() { 96 | if (paint == null) { 97 | textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); 98 | textPaint.setColor(Color.WHITE); 99 | textPaint.setTextSize(SystemUtil.dp(15)); 100 | paint = new Paint(Paint.ANTI_ALIAS_FLAG); 101 | eraser = new Paint(Paint.ANTI_ALIAS_FLAG); 102 | eraser.setColor(0); 103 | eraser.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); 104 | eraser2 = new Paint(Paint.ANTI_ALIAS_FLAG); 105 | eraser2.setColor(0); 106 | eraser2.setStyle(Paint.Style.STROKE); 107 | eraser2.setStrokeWidth(SystemUtil.dp(28)); 108 | eraser2.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); 109 | backgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 110 | backgroundPaint.setStyle(Paint.Style.STROKE); 111 | backgroundPaint.setStrokeWidth(SystemUtil.dp(1)); 112 | } 113 | backgroundPaint.setColor(borderColor); 114 | setCheckOffset(SystemUtil.dp(1)); 115 | setDrawBackground(true); 116 | drawBitmap = 117 | Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_4444); 118 | bitmapCanvas = new Canvas(drawBitmap); 119 | checkBitmap = 120 | Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_4444); 121 | checkCanvas = new Canvas(checkBitmap); 122 | } 123 | 124 | public void setProgress(float value) { 125 | progress = value; 126 | invalidate(); 127 | } 128 | 129 | public void setDrawBackground(boolean value) { 130 | drawBackground = value; 131 | } 132 | 133 | public void setCheckOffset(int value) { 134 | checkOffset = value; 135 | } 136 | 137 | public void setSize(int size) { 138 | this.size = size; 139 | } 140 | 141 | public float getProgress() { 142 | return progress; 143 | } 144 | 145 | public void setNormalColor(int value) { 146 | normalColor = value; 147 | } 148 | 149 | private void cancelCheckAnimator() { 150 | if (checkAnimator != null) { 151 | checkAnimator.cancel(); 152 | } 153 | } 154 | 155 | private void animateToCheckedState(boolean newCheckedState) { 156 | isCheckAnimation = newCheckedState; 157 | checkAnimator = ObjectAnimator.ofFloat(this, "progress", newCheckedState ? 1 : 0); 158 | checkAnimator.setDuration(300); 159 | checkAnimator.start(); 160 | } 161 | 162 | @Override 163 | protected void onAttachedToWindow() { 164 | super.onAttachedToWindow(); 165 | attachedToWindow = true; 166 | } 167 | 168 | @Override 169 | protected void onDetachedFromWindow() { 170 | super.onDetachedFromWindow(); 171 | attachedToWindow = false; 172 | } 173 | 174 | @Override 175 | protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 176 | super.onLayout(changed, left, top, right, bottom); 177 | } 178 | 179 | public void setChecked(boolean checked, boolean animated) { 180 | if (checked == isChecked) { 181 | return; 182 | } 183 | isChecked = checked; 184 | if (!isChecked) { 185 | if (unchecked_invisible) { 186 | setVisibility(View.INVISIBLE); 187 | } 188 | } else { 189 | setVisibility(VISIBLE); 190 | } 191 | refresh(animated); 192 | } 193 | 194 | public void refresh(boolean animated) { 195 | if (attachedToWindow && animated) { 196 | animateToCheckedState(isChecked); 197 | } else { 198 | cancelCheckAnimator(); 199 | setProgress(isChecked ? 1.0f : 0.0f); 200 | } 201 | } 202 | 203 | public boolean isChecked() { 204 | return isChecked; 205 | } 206 | 207 | @Override 208 | protected void onDraw(Canvas canvas) { 209 | if (getVisibility() != VISIBLE) { 210 | return; 211 | } 212 | if (drawBackground || progress != 0) { 213 | drawBitmap.eraseColor(0); 214 | float radX = (getMeasuredWidth() - getPaddingLeft() - getPaddingRight()) / 2; 215 | float radY = (getMeasuredHeight() - getPaddingTop() - getPaddingBottom()) / 2; 216 | float rad = Math.min(radX, radY); 217 | float paddingLeft = getPaddingLeft() + radX - rad; 218 | float paddingTop = getPaddingTop() + radY - rad; 219 | float drawRad = rad; 220 | 221 | float roundProgress = progress >= 0.5f ? 1.0f : progress / 0.5f; 222 | float checkProgress = progress < 0.5f ? 0.0f : (progress - 0.5f) / 0.5f; 223 | 224 | float roundProgressCheckState = isCheckAnimation ? progress : (1.0f - progress); 225 | if (roundProgressCheckState < progressBounceDiff) { 226 | drawRad -= SystemUtil.dp(2) * roundProgressCheckState / progressBounceDiff; 227 | } else if (roundProgressCheckState < progressBounceDiff * 2) { 228 | drawRad -= SystemUtil.dp(2) - SystemUtil.dp(2) 229 | * (roundProgressCheckState - progressBounceDiff) / progressBounceDiff; 230 | } 231 | if (drawBackground) { 232 | paint.setColor(unCheckedColor); 233 | canvas.drawCircle(paddingLeft + rad, paddingTop + rad, drawRad 234 | - SystemUtil.dp(1), paint); 235 | canvas.drawCircle(paddingLeft + rad, paddingTop + rad, drawRad 236 | - SystemUtil.dp(1), backgroundPaint); 237 | } 238 | paint.setColor(isEnabled() ? normalColor : disabledColor); 239 | bitmapCanvas.drawCircle(rad, rad, showBorderInCheckState ? rad - SystemUtil.dp(1.2f) : rad, 240 | paint); 241 | bitmapCanvas.drawCircle(rad, rad, 242 | showBorderInCheckState 243 | ? (rad - SystemUtil.dp(1.2f)) * (1 - roundProgress) 244 | : rad * (1 - roundProgress), 245 | eraser); 246 | canvas.drawBitmap(drawBitmap, paddingLeft, paddingTop, null); 247 | 248 | checkBitmap.eraseColor(0); 249 | if (textLayout != null) { 250 | checkCanvas.save(); 251 | checkCanvas.translate(rad - (textWidth) / 2, 252 | rad - (textHeight) / 2); 253 | textLayout.draw(checkCanvas); 254 | checkCanvas.restore(); 255 | } else if (checkDrawable != null) { 256 | int w = checkDrawable.getIntrinsicWidth(); 257 | int h = checkDrawable.getIntrinsicHeight(); 258 | int x = (getMeasuredWidth() - w) / 2; 259 | int y = (getMeasuredHeight() - h) / 2; 260 | 261 | checkDrawable.setBounds(x, y + checkOffset, x + w, y + h + checkOffset); 262 | checkDrawable.draw(checkCanvas); 263 | } 264 | eraser2.setStrokeWidth(rad); 265 | checkCanvas.drawCircle(rad, 266 | rad, 267 | rad * checkProgress + rad / 2, eraser2); 268 | 269 | canvas.drawBitmap(checkBitmap, paddingLeft, paddingTop, null); 270 | } 271 | } 272 | 273 | public void setText(String text) { 274 | if (!TextUtils.isEmpty(text)) { 275 | textLayout = 276 | new StaticLayout(text, textPaint, 277 | SystemUtil.dp(100), Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); 278 | if (textLayout.getLineCount() > 0) { 279 | textLeft = textLayout.getLineLeft(0); 280 | textWidth = textLayout.getLineWidth(0); 281 | textHeight = textLayout.getLineBottom(0); 282 | invalidate(); 283 | } else { 284 | textLayout = null; 285 | } 286 | } 287 | } 288 | } 289 | -------------------------------------------------------------------------------- /imagepicker/src/main/java/com/imnjh/imagepicker/widget/ClipZoomImageView.java: -------------------------------------------------------------------------------- 1 | package com.imnjh.imagepicker.widget; 2 | 3 | import android.content.Context; 4 | import android.graphics.Bitmap; 5 | import android.graphics.Canvas; 6 | import android.graphics.Matrix; 7 | import android.graphics.RectF; 8 | import android.graphics.drawable.Drawable; 9 | import android.util.AttributeSet; 10 | import android.view.GestureDetector; 11 | import android.view.GestureDetector.SimpleOnGestureListener; 12 | import android.view.MotionEvent; 13 | import android.view.ScaleGestureDetector; 14 | import android.view.ScaleGestureDetector.OnScaleGestureListener; 15 | import android.view.View; 16 | import android.view.View.OnTouchListener; 17 | import android.view.ViewTreeObserver; 18 | import android.widget.ImageView; 19 | 20 | /** 21 | * Created by Martin on 2017/1/17. 22 | */ 23 | public class ClipZoomImageView extends ImageView implements 24 | OnScaleGestureListener, OnTouchListener, 25 | ViewTreeObserver.OnGlobalLayoutListener { 26 | public static float SCALE_MAX = 4.0f; 27 | private static float SCALE_MID = 2.0f; 28 | 29 | private float initScale = 1.0f; 30 | private boolean once = true; 31 | 32 | private final float[] matrixValues = new float[9]; 33 | 34 | private ScaleGestureDetector scaleGestureDetector = null; 35 | private final Matrix scaleMatrix = new Matrix(); 36 | 37 | private GestureDetector gestureDetector; 38 | private boolean isAutoScale; 39 | 40 | private int touchSlop; 41 | 42 | private float mLastX; 43 | private float mLastY; 44 | 45 | private boolean isCanDrag; 46 | private int lastPointerCount; 47 | private int horizontalPadding; 48 | 49 | 50 | public ClipZoomImageView(Context context) { 51 | this(context, null); 52 | } 53 | 54 | public ClipZoomImageView(Context context, AttributeSet attrs) { 55 | super(context, attrs); 56 | 57 | setScaleType(ScaleType.MATRIX); 58 | gestureDetector = new GestureDetector(context, 59 | new SimpleOnGestureListener() { 60 | 61 | @Override 62 | public boolean onDoubleTap(MotionEvent e) { 63 | if (isAutoScale == true) 64 | return true; 65 | 66 | float x = e.getX(); 67 | float y = e.getY(); 68 | if (getScale() < SCALE_MID) { 69 | ClipZoomImageView.this.postDelayed( 70 | new AutoScaleRunnable(SCALE_MID, x, y), 16); 71 | isAutoScale = true; 72 | } else { 73 | ClipZoomImageView.this.postDelayed( 74 | new AutoScaleRunnable(initScale, x, y), 16); 75 | isAutoScale = true; 76 | } 77 | 78 | return true; 79 | } 80 | }); 81 | scaleGestureDetector = new ScaleGestureDetector(context, this); 82 | this.setOnTouchListener(this); 83 | } 84 | 85 | private class AutoScaleRunnable implements Runnable { 86 | static final float BIGGER = 1.07f; 87 | static final float SMALLER = 0.93f; 88 | private float targetScale; 89 | private float tmpScale; 90 | 91 | private float x; 92 | private float y; 93 | 94 | public AutoScaleRunnable(float targetScale, float x, float y) { 95 | this.targetScale = targetScale; 96 | this.x = x; 97 | this.y = y; 98 | if (getScale() < this.targetScale) { 99 | tmpScale = BIGGER; 100 | } else { 101 | tmpScale = SMALLER; 102 | } 103 | 104 | } 105 | 106 | @Override 107 | public void run() { 108 | scaleMatrix.postScale(tmpScale, tmpScale, x, y); 109 | checkBorder(); 110 | setImageMatrix(scaleMatrix); 111 | 112 | final float currentScale = getScale(); 113 | if (((tmpScale > 1f) && (currentScale < targetScale)) 114 | || ((tmpScale < 1f) && (targetScale < currentScale))) { 115 | ClipZoomImageView.this.postDelayed(this, 16); 116 | } else { 117 | final float deltaScale = targetScale / currentScale; 118 | scaleMatrix.postScale(deltaScale, deltaScale, x, y); 119 | checkBorder(); 120 | setImageMatrix(scaleMatrix); 121 | isAutoScale = false; 122 | } 123 | 124 | } 125 | } 126 | 127 | @Override 128 | public boolean onScale(ScaleGestureDetector detector) { 129 | float scale = getScale(); 130 | float scaleFactor = detector.getScaleFactor(); 131 | 132 | if (getDrawable() == null) 133 | return true; 134 | 135 | if ((scale < SCALE_MAX && scaleFactor > 1.0f) 136 | || (scale > initScale && scaleFactor < 1.0f)) { 137 | if (scaleFactor * scale < initScale) { 138 | scaleFactor = initScale / scale; 139 | } 140 | if (scaleFactor * scale > SCALE_MAX) { 141 | scaleFactor = SCALE_MAX / scale; 142 | } 143 | scaleMatrix.postScale(scaleFactor, scaleFactor, 144 | detector.getFocusX(), detector.getFocusY()); 145 | checkBorder(); 146 | setImageMatrix(scaleMatrix); 147 | } 148 | return true; 149 | } 150 | 151 | private RectF getMatrixRectF() { 152 | Matrix matrix = scaleMatrix; 153 | RectF rect = new RectF(); 154 | Drawable d = getDrawable(); 155 | if (null != d) { 156 | rect.set(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight()); 157 | matrix.mapRect(rect); 158 | } 159 | return rect; 160 | } 161 | 162 | @Override 163 | public boolean onScaleBegin(ScaleGestureDetector detector) { 164 | return true; 165 | } 166 | 167 | @Override 168 | public void onScaleEnd(ScaleGestureDetector detector) {} 169 | 170 | @Override 171 | public boolean onTouch(View v, MotionEvent event) { 172 | if (gestureDetector.onTouchEvent(event)) 173 | return true; 174 | scaleGestureDetector.onTouchEvent(event); 175 | 176 | float x = 0, y = 0; 177 | final int pointerCount = event.getPointerCount(); 178 | for (int i = 0; i < pointerCount; i++) { 179 | x += event.getX(i); 180 | y += event.getY(i); 181 | } 182 | x = x / pointerCount; 183 | y = y / pointerCount; 184 | 185 | if (pointerCount != lastPointerCount) { 186 | isCanDrag = false; 187 | mLastX = x; 188 | mLastY = y; 189 | } 190 | 191 | lastPointerCount = pointerCount; 192 | switch (event.getAction()) { 193 | case MotionEvent.ACTION_MOVE: 194 | float dx = x - mLastX; 195 | float dy = y - mLastY; 196 | 197 | if (!isCanDrag) { 198 | isCanDrag = isCanDrag(dx, dy); 199 | } 200 | if (isCanDrag) { 201 | if (getDrawable() != null) { 202 | 203 | RectF rectF = getMatrixRectF(); 204 | if (rectF.width() <= getWidth() - horizontalPadding * 2) { 205 | dx = 0; 206 | } 207 | if (rectF.height() <= getHeight() - getHVerticalPadding() 208 | * 2) { 209 | dy = 0; 210 | } 211 | scaleMatrix.postTranslate(dx, dy); 212 | checkBorder(); 213 | setImageMatrix(scaleMatrix); 214 | } 215 | } 216 | mLastX = x; 217 | mLastY = y; 218 | break; 219 | 220 | case MotionEvent.ACTION_UP: 221 | case MotionEvent.ACTION_CANCEL: 222 | lastPointerCount = 0; 223 | break; 224 | } 225 | 226 | return true; 227 | } 228 | 229 | public final float getScale() { 230 | scaleMatrix.getValues(matrixValues); 231 | return matrixValues[Matrix.MSCALE_X]; 232 | } 233 | 234 | @Override 235 | protected void onAttachedToWindow() { 236 | super.onAttachedToWindow(); 237 | getViewTreeObserver().addOnGlobalLayoutListener(this); 238 | } 239 | 240 | @Override 241 | protected void onDetachedFromWindow() { 242 | super.onDetachedFromWindow(); 243 | getViewTreeObserver().removeGlobalOnLayoutListener(this); 244 | } 245 | 246 | @Override 247 | public void onGlobalLayout() { 248 | if (once) { 249 | Drawable d = getDrawable(); 250 | if (d == null) 251 | return; 252 | int width = getWidth(); 253 | int height = getHeight(); 254 | int drawableW = d.getIntrinsicWidth(); 255 | int drawableH = d.getIntrinsicHeight(); 256 | float scale = 1.0f; 257 | 258 | int frameSize = getWidth() - horizontalPadding * 2; 259 | 260 | if (drawableW > frameSize && drawableH < frameSize) { 261 | scale = 1.0f * frameSize / drawableH; 262 | } else if (drawableH > frameSize && drawableW < frameSize) { 263 | scale = 1.0f * frameSize / drawableW; 264 | } else if (drawableW > frameSize && drawableH > frameSize) { 265 | float scaleW = frameSize * 1.0f / drawableW; 266 | float scaleH = frameSize * 1.0f / drawableH; 267 | scale = Math.max(scaleW, scaleH); 268 | } 269 | 270 | if (drawableW < frameSize && drawableH > frameSize) { 271 | scale = 1.0f * frameSize / drawableW; 272 | } else if (drawableH < frameSize && drawableW > frameSize) { 273 | scale = 1.0f * frameSize / drawableH; 274 | } else if (drawableW < frameSize && drawableH < frameSize) { 275 | float scaleW = 1.0f * frameSize / drawableW; 276 | float scaleH = 1.0f * frameSize / drawableH; 277 | scale = Math.max(scaleW, scaleH); 278 | } 279 | 280 | initScale = scale; 281 | SCALE_MID = initScale * 2; 282 | SCALE_MAX = initScale * 4; 283 | scaleMatrix.postTranslate((width - drawableW) / 2, 284 | (height - drawableH) / 2); 285 | scaleMatrix.postScale(scale, scale, getWidth() / 2, 286 | getHeight() / 2); 287 | 288 | setImageMatrix(scaleMatrix); 289 | once = false; 290 | } 291 | } 292 | 293 | public Bitmap clip() { 294 | Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(), 295 | Bitmap.Config.ARGB_8888); 296 | Canvas canvas = new Canvas(bitmap); 297 | draw(canvas); 298 | return Bitmap.createBitmap(bitmap, horizontalPadding, 299 | getHVerticalPadding(), getWidth() - 2 * horizontalPadding, 300 | getWidth() - 2 * horizontalPadding); 301 | } 302 | 303 | private void checkBorder() { 304 | RectF rect = getMatrixRectF(); 305 | float deltaX = 0; 306 | float deltaY = 0; 307 | 308 | int width = getWidth(); 309 | int height = getHeight(); 310 | 311 | if (rect.width() + 0.01 >= width - 2 * horizontalPadding) { 312 | if (rect.left > horizontalPadding) { 313 | deltaX = -rect.left + horizontalPadding; 314 | } 315 | 316 | if (rect.right < width - horizontalPadding) { 317 | deltaX = width - horizontalPadding - rect.right; 318 | } 319 | } 320 | 321 | if (rect.height() >= height - 2 * getHVerticalPadding()) { 322 | if (rect.top > getHVerticalPadding()) { 323 | deltaY = -rect.top + getHVerticalPadding(); 324 | } 325 | 326 | if (rect.bottom < height - getHVerticalPadding()) { 327 | deltaY = height - getHVerticalPadding() - rect.bottom; 328 | } 329 | } 330 | 331 | scaleMatrix.postTranslate(deltaX, deltaY); 332 | } 333 | 334 | private boolean isCanDrag(float dx, float dy) { 335 | return Math.sqrt((dx * dx) + (dy * dy)) >= touchSlop; 336 | } 337 | 338 | public void setHorizontalPadding(int mHorizontalPadding) { 339 | this.horizontalPadding = mHorizontalPadding; 340 | } 341 | 342 | private int getHVerticalPadding() { 343 | return (getHeight() - (getWidth() - 2 * horizontalPadding)) / 2; 344 | } 345 | } 346 | --------------------------------------------------------------------------------