diff --git a/omniNotes/src/main/java/it/feio/android/omninotes/mypackage/FastScrollDelegate.java b/omniNotes/src/main/java/it/feio/android/omninotes/mypackage/FastScrollDelegate.java new file mode 100644 index 0000000000..654b3ab469 --- /dev/null +++ b/omniNotes/src/main/java/it/feio/android/omninotes/mypackage/FastScrollDelegate.java @@ -0,0 +1,802 @@ +package it.feio.android.omninotes.mypackage; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.ColorFilter; +import android.graphics.Interpolator; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.GradientDrawable; +import android.graphics.drawable.InsetDrawable; +import android.graphics.drawable.StateListDrawable; +import android.support.v4.view.ViewCompat; +import android.text.TextUtils.TruncateAt; +import android.util.Log; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewConfiguration; +import android.view.animation.AnimationUtils; +import android.widget.AbsListView; +import android.widget.PopupWindow; +import android.widget.TextView; + +/** + * + * https://github.com/Mixiaoxiao/FastScroll-Everywhere FastScrollDelegate + * + * @author Mixiaoxiao 2016-08-28 + */ +public class FastScrollDelegate { + + @SuppressWarnings("unused") + private void log(String msg) { + Log.d("FastScrollDelegate", msg); + } + + public static interface FastScrollable { + + public void superOnTouchEvent(MotionEvent event); + + public int superComputeVerticalScrollExtent(); + + public int superComputeVerticalScrollOffset(); + + public int superComputeVerticalScrollRange(); + + public View getFastScrollableView(); + + public FastScrollDelegate getFastScrollDelegate(); + + public void setNewFastScrollDelegate(FastScrollDelegate newDelegate); + } + + public static interface OnFastScrollListener { + public void onFastScrollStart(View view, FastScrollDelegate delegate); + + public void onFastScrolled(View view, FastScrollDelegate delegate, int touchDeltaY, int viewScrollDeltaY, + float scrollPercent); + + public void onFastScrollEnd(View view, FastScrollDelegate delegate); + } + + // codes from FastScroller (for AbsListView) + /** Duration of fade-out animation. */ + public static int FASTSCROLLER_DURATION_FADE_OUT = 300; + /** Duration of fade-in animation. */ + public static int FASTSCROLLER_DURATION_FADE_IN = 150; + /** Inactivity timeout before fading controls. */ + public static long FASTSCROLLER_FADE_TIMEOUT = 1500; + + private static final int[] DRAWABLE_STATE_PRESSED = new int[] { android.R.attr.state_pressed }; + private static final int[] DRAWABLE_STATE_DEFAULT = new int[] {}; + + private final View mView; + private final float mDensity; + private float mDownY; + private final Rect mThumbRect; + private Drawable mThumbDrawable; + private final FastScrollable mFastScrollable; + private int mThumbMinHeight; + private final ScrollabilityCache mScrollCache; + private IndicatorPopup mIndicatorPopup; + private boolean mThumbDynamicHeight; + + private OnFastScrollListener mFastScrollListener; + private boolean mIsHanlingTouchEvent = false; + + private FastScrollDelegate(final FastScrollable fastScrollable, int width, int height, Drawable thumbDrawable, + boolean isDynamicHeight) { + super(); + this.mView = fastScrollable.getFastScrollableView(); + mView.setVerticalScrollBarEnabled(false); + Context context = mView.getContext(); + this.mDensity = context.getResources().getDisplayMetrics().density; + this.mThumbMinHeight = dp2px(FASTSCROLLER_MIN_HEIGHT_DP); + this.mThumbRect = new Rect(0, 0, width, height); + this.mThumbDrawable = thumbDrawable; + this.mFastScrollable = fastScrollable; + this.mScrollCache = new ScrollabilityCache(ViewConfiguration.get(context), mView); + this.mThumbDynamicHeight = isDynamicHeight; + } + + // =========================================================== + // Useful methods + // =========================================================== + public void setThumbDrawable(Drawable drawable) { + if (drawable == null) { + throw new IllegalArgumentException("setThumbDrawable must NOT be NULL"); + } + mThumbDrawable = drawable; + updateThumbRect(0); + } + + public void setThumbSize(int widthDp, int heightDp) { + mThumbRect.left = mThumbRect.right - dp2px(widthDp); + mThumbMinHeight = dp2px(heightDp); + updateThumbRect(0); + } + + public void setThumbDynamicHeight(boolean isDynamicHeight) { + if (mThumbDynamicHeight != isDynamicHeight) { + mThumbDynamicHeight = isDynamicHeight; + updateThumbRect(0); + } + } + + public void setOnFastScrollListener(OnFastScrollListener l) { + mFastScrollListener = l; + } + + // =========================================================== + // Delegate + // =========================================================== + + // See View.class + public boolean awakenScrollBars() { + return awakenScrollBars(FASTSCROLLER_FADE_TIMEOUT);// Cache.scrollBarDefaultDelayBeforeFade + } + + // See View.class + private boolean initialAwakenScrollBars() { + return awakenScrollBars(mScrollCache.scrollBarDefaultDelayBeforeFade * 4); + } + + // See View.class + public boolean awakenScrollBars(long startDelay) { + ViewCompat.postInvalidateOnAnimation(mView); + // log("awakenScrollBars call startDelay->" + startDelay); + if (!mIsHanlingTouchEvent) { + if (mScrollCache.state == ScrollabilityCache.OFF) { + // FIXME: this is copied from WindowManagerService. + // We should get this value from the system when it + // is possible to do so. + final int KEY_REPEAT_FIRST_DELAY = 750; + startDelay = Math.max(KEY_REPEAT_FIRST_DELAY, startDelay); + } + // Tell mScrollCache when we should start fading. This may + // extend the fade start time if one was already scheduled + long fadeStartTime = AnimationUtils.currentAnimationTimeMillis() + startDelay; + mScrollCache.fadeStartTime = fadeStartTime; + mScrollCache.state = ScrollabilityCache.ON; + // Schedule our fader to run, unscheduling any old ones first + // if (mAttachInfo != null) { + // mAttachInfo.mHandler.removeCallbacks(scrollCache); + // mAttachInfo.mHandler.postAtTime(scrollCache, fadeStartTime); + // } + mView.removeCallbacks(mScrollCache); + mView.postDelayed(mScrollCache, fadeStartTime - AnimationUtils.currentAnimationTimeMillis()); + } + return false; + } + + // =========================================================== + // TouchEvent Delegate + // =========================================================== + public boolean onInterceptTouchEvent(MotionEvent ev) { + return onInterceptTouchEventInternal(ev); + } + + public boolean onTouchEvent(MotionEvent event) { + return onTouchEventInternal(event); + } + + // =========================================================== + // TouchEvent Internal + // =========================================================== + private boolean onInterceptTouchEventInternal(MotionEvent ev) { + final int action = ev.getActionMasked(); + if (action == MotionEvent.ACTION_DOWN) { + // Just check if hit the thumb + return onTouchEventInternal(ev); + } + return false; + } + + private boolean onTouchEventInternal(MotionEvent event) { + final int action = event.getActionMasked(); + final float y = event.getY(); + switch (action) { + case MotionEvent.ACTION_DOWN: { + // log("onTouchEvent ACTION_DOWN"); + if (mScrollCache.state == ScrollabilityCache.OFF) { + mIsHanlingTouchEvent = false; + return false; + } + if (!mIsHanlingTouchEvent) { + updateThumbRect(0); + final float x = event.getX(); + // Check if hit the thumb, Rect.contains(int x ,int y) is NOT + // exact + if (y >= mThumbRect.top && y <= mThumbRect.bottom && x >= mThumbRect.left && x <= mThumbRect.right) { + mIsHanlingTouchEvent = true; + mDownY = y; + // try to stop scroll + // step 0: call super ACTION_DOWN + mFastScrollable.superOnTouchEvent(event); + // step 1: call super ACTION_CANCEL + MotionEvent fakeCancelMotionEvent = MotionEvent.obtain(event); + fakeCancelMotionEvent.setAction(MotionEvent.ACTION_CANCEL); + mFastScrollable.superOnTouchEvent(fakeCancelMotionEvent); + fakeCancelMotionEvent.recycle(); + // update ThumbDrawable state and report + // OnFastScrollListener + setPressedThumb(true); + // Call updateThumbRect to report + // OnFastScrollListener.onFastScrolled + updateThumbRect(0, true); + // Do NOT fade Thumb + mView.removeCallbacks(mScrollCache); + } + } + break; + } + case MotionEvent.ACTION_MOVE: { + if (mIsHanlingTouchEvent) { + final int touchDeltaY = Math.round(y - mDownY); + if (touchDeltaY != 0) { + updateThumbRect(touchDeltaY); + // only touchDeltaY != 0, we save the touchY, to Avoid + // accuracy error + mDownY = y; + } + } + break; + } + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: { + if (mIsHanlingTouchEvent) { + setPressedThumb(false); + mIsHanlingTouchEvent = false; + awakenScrollBars(); + } + break; + } + }// End switch + if (mIsHanlingTouchEvent) { + mView.invalidate(); + mView.getParent().requestDisallowInterceptTouchEvent(true); + return true; + } + return false; + } + + // =========================================================== + // Delegate + // =========================================================== + /** Call after View.dispatchDraw() **/ + public void dispatchDrawOver(Canvas canvas) { + onDrawScrollBars(canvas); + } + + public void onAttachedToWindow() { + initialAwakenScrollBars(); + } + + @SuppressLint("MissingSuperCall") + // fuck this lint warning + public void onDetachedFromWindow() { + if (mIndicatorPopup != null) { + mIndicatorPopup.dismiss(); + } + } + + /** + * Please check if the delegate is NULL before call this method If your view + * has the android:visibility attr in xml, this method in view is called + * before your delegate is created + */ + public void onVisibilityChanged(View changedView, int visibility) { + if (visibility == View.VISIBLE) { + // This compat method is interesting, KK has method + // isAttachedToWindow + // < KK is view.getWindowToken() != null + if (ViewCompat.isAttachedToWindow(mView)) { + // Same as mAttachInfo != null + initialAwakenScrollBars(); + } + + } + } + + public void onWindowVisibilityChanged(int visibility) { + if (visibility == View.VISIBLE) { + initialAwakenScrollBars(); + } + } + + // =========================================================== + // Internal + // =========================================================== + + private void onDrawScrollBars(Canvas canvas) { + boolean invalidate = false; + if (mIsHanlingTouchEvent) { + mThumbDrawable.setAlpha(255); + } else { + // Copy from View.class + final ScrollabilityCache cache = mScrollCache; + // cache.scrollBar = mThumbDrawable; + final int state = cache.state; + if (state == ScrollabilityCache.OFF) { + return; + } + if (state == ScrollabilityCache.FADING) { + // We're fading -- get our fade interpolation + if (cache.interpolatorValues == null) { + cache.interpolatorValues = new float[1]; + } + float[] values = cache.interpolatorValues; + // Stops the animation if we're done + if (cache.scrollBarInterpolator.timeToValues(values) == Interpolator.Result.FREEZE_END) { + cache.state = ScrollabilityCache.OFF; + } else { + // in View.class is "cache.scrollBar.mutate()" + mThumbDrawable.setAlpha(Math.round(values[0])); + } + invalidate = true; + } else { + // reset alpha, in View.class is "cache.scrollBar.mutate()" + mThumbDrawable.setAlpha(255); + } + } + + // Draw the thumb + if (updateThumbRect(0)) { + final int scrollY = mView.getScrollY(); + final int scrollX = mView.getScrollX(); + mThumbDrawable.setBounds(mThumbRect.left + scrollX, mThumbRect.top + scrollY, mThumbRect.right + scrollX, + mThumbRect.bottom + scrollY); + mThumbDrawable.draw(canvas); + } + if (invalidate) { + mView.invalidate(); + } + + } + + private void setPressedThumb(boolean pressed) { + mThumbDrawable.setState(pressed ? DRAWABLE_STATE_PRESSED : DRAWABLE_STATE_DEFAULT); + mView.invalidate(); + if (mIndicatorPopup != null) { + if (pressed) { + mIndicatorPopup.show(); + } else { + mIndicatorPopup.dismiss(); + } + } + if (mFastScrollListener != null) { + if (pressed) { + mFastScrollListener.onFastScrollStart(mView, this); + } else { + mFastScrollListener.onFastScrollEnd(mView, this); + } + + } + } + + private boolean updateThumbRect(int touchDeltaY) { + return updateThumbRect(touchDeltaY, false); + } + + /** + * updateThumbRect + * + * @param touchDeltaY + * ,if touchDeltaY != 0, will report + * FastScrollListener.onFastScrolled + * @param forceReportFastScrolled + * , if true, will force report FastScrollListener.onFastScrolled + * @return false:Thumb return false means no need to draw thumb + */ + private boolean updateThumbRect(int touchDeltaY, boolean forceReportFastScrolled) { + final int thumbWidth = mThumbRect.width(); + mThumbRect.right = mView.getWidth(); + mThumbRect.left = mThumbRect.right - thumbWidth; + final int scrollRange = mFastScrollable.superComputeVerticalScrollRange();// 整体的全部高度 + if (scrollRange <= 0) {// no content, 仅在有内容的时候绘制thumb + return false; + } + final int scrollOffset = mFastScrollable.superComputeVerticalScrollOffset();// 上方已经滑动出本身范围的高度 + final int scrollExtent = mFastScrollable.superComputeVerticalScrollExtent();// 当前显示区域的高度 + final int scrollMaxOffset = scrollRange - scrollExtent; + if (scrollMaxOffset <= 0) {// can not scroll, 内容部分不够或刚好充满 + return false; + } + final float scrollPercent = scrollOffset * 1f / (scrollMaxOffset); + final float visiblePercent = scrollExtent * 1f / scrollRange; + // log("scrollPercent->" + scrollPercent + " visiblePercent->" + + // visiblePercent); + final int viewHeight = mView.getHeight(); + final int thumbHeight = mThumbDynamicHeight ? Math + .max(mThumbMinHeight, Math.round(visiblePercent * viewHeight)) : mThumbMinHeight; + mThumbRect.bottom = mThumbRect.top + thumbHeight; + final int thumbTop = Math.round((viewHeight - thumbHeight) * scrollPercent); + mThumbRect.offsetTo(mThumbRect.left, thumbTop); + + if (mIndicatorPopup != null) { + mIndicatorPopup.setOffset(mView.getWidth() - mIndicatorPopup.getPopupSize() - mThumbRect.width(), + -viewHeight + mThumbRect.centerY() - mIndicatorPopup.getPopupSize()); + } + if (touchDeltaY != 0) {// compute the ScrollOffset, 按touchDeltaY计算滚动 + int newThumbTop = thumbTop + touchDeltaY; + final int minThumbTop = 0; + final int maxThumbTop = viewHeight - thumbHeight; + if (newThumbTop > maxThumbTop) { + newThumbTop = maxThumbTop; + } else if (newThumbTop < minThumbTop) { + newThumbTop = minThumbTop; + } + + final float newScrollPercent = newThumbTop * 1f / maxThumbTop;// 百分比 + final int newScrollOffset = Math.round((scrollRange - scrollExtent) * newScrollPercent); + final int viewScrollDeltaY = newScrollOffset - scrollOffset; + if (mView instanceof AbsListView) { + // Call scrollBy to AbsListView , not work correctly + ((AbsListView) mView).smoothScrollBy(viewScrollDeltaY, 0); + } else { + mView.scrollBy(0, viewScrollDeltaY); + + } + if (mFastScrollListener != null) { + mFastScrollListener.onFastScrolled(mView, this, touchDeltaY, viewScrollDeltaY, newScrollPercent); + } + } else { + if (forceReportFastScrolled) { + if (mFastScrollListener != null) { + mFastScrollListener.onFastScrolled(mView, this, 0, 0, scrollPercent); + } + } + } + return true; + } + + /** Copy from View.class **/ + private static class ScrollabilityCache implements Runnable { + /*** Scrollbars are not visible */ + public static final int OFF = 0; + /** * Scrollbars are visible */ + public static final int ON = 1; + /** * Scrollbars are fading away */ + public static final int FADING = 2; + public final int scrollBarDefaultDelayBeforeFade; + public final int scrollBarFadeDuration; + + // public ScrollBarDrawable scrollBar; + // public Drawable scrollBar; + public float[] interpolatorValues; + public View host; + + public final Interpolator scrollBarInterpolator = new Interpolator(1, 2); + + private static final float[] OPAQUE = { 255 }; + private static final float[] TRANSPARENT = { 0.0f }; + + /** + * When fading should start. This time moves into the future every time + * a new scroll happens. Measured based on SystemClock.uptimeMillis() + */ + public long fadeStartTime; + + /** * The current state of the scrollbars: ON, OFF, or FADING */ + public int state = OFF; + + public ScrollabilityCache(ViewConfiguration configuration, View host) { + // scrollBarSize = configuration.getScaledScrollBarSize(); + scrollBarDefaultDelayBeforeFade = ViewConfiguration.getScrollDefaultDelay(); + scrollBarFadeDuration = ViewConfiguration.getScrollBarFadeDuration(); + this.host = host; + } + + public void run() { + long now = AnimationUtils.currentAnimationTimeMillis(); + if (now >= fadeStartTime) { + + // the animation fades the scrollbars out by changing + // the opacity (alpha) from fully opaque to fully + // transparent + int nextFrame = (int) now; + int framesCount = 0; + + Interpolator interpolator = scrollBarInterpolator; + + // Start opaque + interpolator.setKeyFrame(framesCount++, nextFrame, OPAQUE); + + // End transparent + nextFrame += scrollBarFadeDuration; + interpolator.setKeyFrame(framesCount, nextFrame, TRANSPARENT); + + state = FADING; + + // Kick off the fade animation + // host.invalidate(true); + host.invalidate(); + } + } + } + + public View getView() { + return mView; + } + + private int dp2px(float dp) { + return (int) (mDensity * dp + 0.5f); + } + + public void setIndicatorText(String indicator) { + if (mIndicatorPopup != null) { + mIndicatorPopup.setIndicatorText(indicator); + } + } + + public void initIndicatorPopup(IndicatorPopup indicatorPopup) { + mIndicatorPopup = indicatorPopup; + } + + // =========================================================== + // IndicatorPopup + // =========================================================== + public static class IndicatorPopup { + + public static class Builder { + + private final float density; + private final View anchor; + private int indicatorPopupColor = COLOR_THUMB_PRESSED; + private int indicatorPopupSize; + private int indicatorTextSize; + private int indicatorMarginRight; + private int indicatorPopupAnimationStyle = FASTSCROLLER_INDICATOR_POPUPANIMATIONSTYLE; + + public Builder(FastScrollDelegate delegate) { + this.anchor = delegate.getView(); + this.density = anchor.getContext().getResources().getDisplayMetrics().density; + indicatorPopupSize = dp2px(FASTSCROLLER_INDICATOR_SIZE_DP); + indicatorTextSize = dp2px(FASTSCROLLER_INDICATOR_TEXTSIZE_DP); + indicatorMarginRight = dp2px(FASTSCROLLER_INDICATOR_MARINRIGHT_DP); + } + + public Builder indicatorPopupColor(int popupColor) { + indicatorPopupColor = popupColor; + return this; + } + + public Builder indicatorPopupSize(int popupSizeDp) { + indicatorPopupSize = dp2px(popupSizeDp); + return this; + } + + public Builder indicatorTextSize(int textSizeDp) { + indicatorTextSize = dp2px(textSizeDp); + return this; + } + + public Builder indicatorMarginRight(int marginRightDp) { + indicatorMarginRight = dp2px(marginRightDp); + return this; + } + + public Builder indicatorPopupAnimationStyle(int animationStyle) { + indicatorPopupAnimationStyle = animationStyle; + return this; + } + + private int dp2px(float dp) { + return (int) (density * dp + 0.5f); + } + + public IndicatorPopup build() { + return new IndicatorPopup(anchor, indicatorPopupColor, indicatorPopupSize, indicatorTextSize, + indicatorMarginRight, indicatorPopupAnimationStyle); + } + + } + + final View anchor; + final int popupSize; + final int marginRight; + final TextView bubbleView; + int xOffset, yOffset; + final PopupWindow popupWindow; + + @SuppressWarnings("deprecation") + private IndicatorPopup(View anchor, int popupColor, int popupSize, int textSize, int marginRight, + int popupAnimationStyle) { + super(); + this.anchor = anchor; + this.popupSize = popupSize; + this.marginRight = marginRight; + this.bubbleView = new TextView(anchor.getContext()); + bubbleView.setGravity(Gravity.CENTER); + bubbleView.setTextColor(Color.WHITE); + bubbleView.setSingleLine(); + bubbleView.setBackgroundDrawable(new BubbleDrawable(popupColor)); + bubbleView.setEllipsize(TruncateAt.END); + bubbleView.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize); + this.popupWindow = new PopupWindow(bubbleView, popupSize, popupSize, false); + popupWindow.setAnimationStyle(popupAnimationStyle); + } + + public int getPopupSize() { + return popupSize; + } + + public void setOffset(int xoff, int yoff) { + this.xOffset = xoff; + this.yOffset = yoff; + if (popupWindow != null && popupWindow.isShowing()) { + popupWindow.update(anchor, xoff - marginRight, yoff, popupSize, popupSize); + } + + } + + public void show() { + if (popupWindow != null && !popupWindow.isShowing()) { + popupWindow.showAsDropDown(anchor, xOffset - marginRight, yOffset); + } + } + + public void dismiss() { + if (popupWindow != null && popupWindow.isShowing()) { + popupWindow.dismiss(); + } + } + + public void setIndicatorText(String indicator) { + bubbleView.setText(indicator); + } + } + + private static class BubbleDrawable extends Drawable { + + private final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Path path = new Path(); + private final RectF rectF = new RectF(); + + public BubbleDrawable(int color) { + super(); + paint.setColor(color); + } + + @Override + public void draw(Canvas canvas) { + canvas.drawPath(path, paint); + } + + @Override + protected void onBoundsChange(Rect bounds) { + super.onBoundsChange(bounds); + final int w = bounds.width(); + final int h = bounds.height(); + path.reset(); + final float radius = Math.min(w, h) / 2f - 1f; + final float cx = w / 2f; + final float cy = h / 2f; + rectF.set(cx - radius, cy - radius, cx + radius, cy + radius); + // path.addArc()//Not work + path.arcTo(rectF, 0, -270, true); + path.lineTo(cx + radius, cy + radius); + path.close(); + } + + @Override + public void setAlpha(int alpha) { + paint.setAlpha(alpha); + } + + @Override + public void setColorFilter(ColorFilter cf) { + paint.setColorFilter(cf); + } + + @Override + public int getOpacity() { + return PixelFormat.TRANSLUCENT; + } + + } + + // ================== + // Builder + // ================== + + public static int FASTSCROLLER_WIDTH_DP = 20;// 12; + public static int FASTSCROLLER_MIN_HEIGHT_DP = 32; + public static int FASTSCROLLER_THUMB_WIDTH = 4; + public static int FASTSCROLLER_THUMB_INSET_TOP_BOTTOM_RIGHT = 4; + + public static int COLOR_THUMB_NORMAL = 0x80808080; + public static int COLOR_THUMB_PRESSED = 0xff03a9f4;// 0xff45c01a; + + private static int FASTSCROLLER_INDICATOR_SIZE_DP = 72; + private static int FASTSCROLLER_INDICATOR_MARINRIGHT_DP = 24; + private static int FASTSCROLLER_INDICATOR_TEXTSIZE_DP = 36; + private static int FASTSCROLLER_INDICATOR_POPUPANIMATIONSTYLE = android.R.style.Animation_Dialog; + + public static class Builder { + private final float density; + private final FastScrollable fastScrollable; + private int width; + private int height; + private boolean isDynamicHeight = true; + private Drawable thumbDrawable; + private int thumbNormalColor = COLOR_THUMB_NORMAL; + private int thumbPressedColor = COLOR_THUMB_PRESSED; + + public Builder(FastScrollable fastScrollable) { + super(); + this.fastScrollable = fastScrollable; + this.density = fastScrollable.getFastScrollableView().getContext().getResources().getDisplayMetrics().density; + width = dp2px(FASTSCROLLER_WIDTH_DP); + height = dp2px(FASTSCROLLER_MIN_HEIGHT_DP); + + } + + public Builder width(float widthDp) { + width = dp2px(widthDp); + return this; + } + + public Builder height(float heightDp) { + height = dp2px(heightDp); + return this; + } + + public Builder thumbNormalColor(int normalColor) { + thumbNormalColor = normalColor; + return this; + } + + public Builder thumbPressedColor(int pressedColor) { + thumbPressedColor = pressedColor; + return this; + } + + public Builder thumbDrawable(Drawable thumb) { + thumbDrawable = thumb; + return this; + } + + public Builder dynamicHeight(boolean isDynamic) { + isDynamicHeight = isDynamic; + return this; + } + + public FastScrollDelegate build() { + if (this.thumbDrawable == null) { + this.thumbDrawable = makeDefaultThumbDrawable(); + } + return new FastScrollDelegate(fastScrollable, width, height, thumbDrawable, isDynamicHeight); + } + + private Drawable makeDefaultThumbDrawable() { + StateListDrawable stateListDrawable = new StateListDrawable(); + GradientDrawable pressedDrawable = new GradientDrawable(); + pressedDrawable.setColor(thumbPressedColor); + final float radius = width / 2f; + final int inset = dp2px(FASTSCROLLER_THUMB_INSET_TOP_BOTTOM_RIGHT);// inset + final int insetLeft = width - inset - dp2px(FASTSCROLLER_THUMB_WIDTH); + pressedDrawable.setCornerRadius(radius); + stateListDrawable.addState(DRAWABLE_STATE_PRESSED, new InsetDrawable(pressedDrawable, insetLeft, inset, + inset, inset)); + GradientDrawable normalDrawable = new GradientDrawable(); + normalDrawable.setColor(thumbNormalColor); + normalDrawable.setCornerRadius(radius); + stateListDrawable.addState(DRAWABLE_STATE_DEFAULT, new InsetDrawable(normalDrawable, insetLeft, inset, + inset, inset)); + return stateListDrawable; + } + + public int dp2px(float dp) { + return (int) (dp * density + 0.5f); + } + } +} \ No newline at end of file diff --git a/omniNotes/src/main/java/it/feio/android/omninotes/mypackage/FastScrollScrollView.java b/omniNotes/src/main/java/it/feio/android/omninotes/mypackage/FastScrollScrollView.java new file mode 100644 index 0000000000..a9bda3a927 --- /dev/null +++ b/omniNotes/src/main/java/it/feio/android/omninotes/mypackage/FastScrollScrollView.java @@ -0,0 +1,160 @@ +package it.feio.android.omninotes.mypackage; + +import android.annotation.SuppressLint; +import android.annotation.TargetApi; +import android.content.Context; +import android.graphics.Canvas; +import android.os.Build; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; +import android.widget.ScrollView; + +import it.feio.android.omninotes.mypackage.FastScrollDelegate.FastScrollable; + +/** + * https://github.com/Mixiaoxiao/FastScroll-Everywhere FastScrollScrollView + * + * @author Mixiaoxiao 2016-08-31 + */ +public class FastScrollScrollView extends ScrollView implements FastScrollable { + + private FastScrollDelegate mFastScrollDelegate; + + // =========================================================== + // Constructors + // =========================================================== + + public FastScrollScrollView(Context context) { + super(context); + createFastScrollDelegate(context); + } + + public FastScrollScrollView(Context context, AttributeSet attrs) { + super(context, attrs); + createFastScrollDelegate(context); + } + + public FastScrollScrollView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + createFastScrollDelegate(context); + } + + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + public FastScrollScrollView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + createFastScrollDelegate(context); + } + + // =========================================================== + // createFastScrollDelegate + // =========================================================== + + private void createFastScrollDelegate(Context context) { + mFastScrollDelegate = new FastScrollDelegate.Builder(this).build(); + } + + // =========================================================== + // Delegate + // =========================================================== + + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + if (mFastScrollDelegate.onInterceptTouchEvent(ev)) { + return true; + } + return super.onInterceptTouchEvent(ev); + } + + @SuppressLint("ClickableViewAccessibility") + @Override + public boolean onTouchEvent(MotionEvent event) { + if (mFastScrollDelegate.onTouchEvent(event)) { + return true; + } + return super.onTouchEvent(event); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + mFastScrollDelegate.onAttachedToWindow(); + } + + @Override + protected void onVisibilityChanged(View changedView, int visibility) { + super.onVisibilityChanged(changedView, visibility); + if (mFastScrollDelegate != null) { + mFastScrollDelegate.onVisibilityChanged(changedView, visibility); + } + } + + @Override + protected void onWindowVisibilityChanged(int visibility) { + super.onWindowVisibilityChanged(visibility); + mFastScrollDelegate.onWindowVisibilityChanged(visibility); + } + + @Override + protected boolean awakenScrollBars() { + return mFastScrollDelegate.awakenScrollBars(); + } + + @Override + protected void dispatchDraw(Canvas canvas) { + super.dispatchDraw(canvas); + mFastScrollDelegate.dispatchDrawOver(canvas); + } + + // =========================================================== + // FastScrollable IMPL, ViewInternalSuperMethods + // =========================================================== + + @Override + public void superOnTouchEvent(MotionEvent event) { + super.onTouchEvent(event); + } + + @Override + public int superComputeVerticalScrollExtent() { + return super.computeVerticalScrollExtent(); + } + + @Override + public int superComputeVerticalScrollOffset() { + return super.computeVerticalScrollOffset(); + } + + @Override + public int superComputeVerticalScrollRange() { + return super.computeVerticalScrollRange(); + } + + @Override + public View getFastScrollableView() { + return this; + } + + /** + * @deprecated use {@link #getFastScrollDelegate()} instead + */ + public FastScrollDelegate getDelegate() { + return getFastScrollDelegate(); + } + + @Override + public FastScrollDelegate getFastScrollDelegate() { + return mFastScrollDelegate; + } + + @Override + public void setNewFastScrollDelegate(FastScrollDelegate newDelegate) { + if (newDelegate == null) { + throw new IllegalArgumentException("setNewFastScrollDelegate must NOT be NULL."); + } + mFastScrollDelegate.onDetachedFromWindow(); + mFastScrollDelegate = newDelegate; + newDelegate.onAttachedToWindow(); + } + +} \ No newline at end of file diff --git a/omniNotes/src/main/res/layout/fragment_detail.xml b/omniNotes/src/main/res/layout/fragment_detail.xml index 11d8a058d0..1a1c10cf91 100644 --- a/omniNotes/src/main/res/layout/fragment_detail.xml +++ b/omniNotes/src/main/res/layout/fragment_detail.xml @@ -91,17 +91,15 @@ android:background="@drawable/bg_card" android:paddingBottom="2dp"> - + - + . --> - + @@ -64,7 +68,8 @@ - + + \ No newline at end of file