Skip to content
Snippets Groups Projects
Verified Commit 95b8f07d authored by Janne Mareike Koschinski's avatar Janne Mareike Koschinski
Browse files

Fixes scrollbars, updates libraries

parent b9b138e5
No related branches found
No related tags found
No related merge requests found
Pipeline #242 passed
Showing
with 1334 additions and 43 deletions
...@@ -110,7 +110,7 @@ android { ...@@ -110,7 +110,7 @@ android {
} }
dependencies { dependencies {
implementation(kotlin("stdlib", "1.2.50")) implementation(kotlin("stdlib", "1.2.51"))
// App Compat // App Compat
withVersion("27.1.1") { withVersion("27.1.1") {
...@@ -166,7 +166,6 @@ dependencies { ...@@ -166,7 +166,6 @@ dependencies {
// UI // UI
implementation("me.zhanghai.android.materialprogressbar", "library", "1.4.2") implementation("me.zhanghai.android.materialprogressbar", "library", "1.4.2")
implementation("com.simplecityapps", "recyclerview-fastscroll", "1.0.18")
withVersion("0.9.6.0") { withVersion("0.9.6.0") {
implementation("com.afollestad.material-dialogs", "core", version) implementation("com.afollestad.material-dialogs", "core", version)
implementation("com.afollestad.material-dialogs", "commons", version) implementation("com.afollestad.material-dialogs", "commons", version)
......
...@@ -161,7 +161,7 @@ ...@@ -161,7 +161,7 @@
}, },
{ {
"name": "Kotlin Standard Library", "name": "Kotlin Standard Library",
"version": "1.2.50", "version": "1.2.51",
"license": { "license": {
"short_name": "Apache-2.0", "short_name": "Apache-2.0",
"full_name": "Apache License" "full_name": "Apache License"
......
...@@ -29,7 +29,6 @@ import android.widget.ImageView ...@@ -29,7 +29,6 @@ import android.widget.ImageView
import android.widget.TextView import android.widget.TextView
import butterknife.BindView import butterknife.BindView
import butterknife.ButterKnife import butterknife.ButterKnife
import com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView
import de.kuschku.libquassel.protocol.BufferId import de.kuschku.libquassel.protocol.BufferId
import de.kuschku.libquassel.protocol.Buffer_Activity import de.kuschku.libquassel.protocol.Buffer_Activity
import de.kuschku.libquassel.protocol.NetworkId import de.kuschku.libquassel.protocol.NetworkId
...@@ -39,6 +38,7 @@ import de.kuschku.quasseldroid.R ...@@ -39,6 +38,7 @@ import de.kuschku.quasseldroid.R
import de.kuschku.quasseldroid.settings.MessageSettings import de.kuschku.quasseldroid.settings.MessageSettings
import de.kuschku.quasseldroid.util.helper.* import de.kuschku.quasseldroid.util.helper.*
import de.kuschku.quasseldroid.util.lists.ListAdapter import de.kuschku.quasseldroid.util.lists.ListAdapter
import de.kuschku.quasseldroid.util.ui.fastscroll.views.FastScrollRecyclerView
import de.kuschku.quasseldroid.viewmodel.data.BufferListItem import de.kuschku.quasseldroid.viewmodel.data.BufferListItem
import de.kuschku.quasseldroid.viewmodel.data.BufferProps import de.kuschku.quasseldroid.viewmodel.data.BufferProps
import de.kuschku.quasseldroid.viewmodel.data.BufferState import de.kuschku.quasseldroid.viewmodel.data.BufferState
......
...@@ -29,7 +29,6 @@ import android.widget.ImageView ...@@ -29,7 +29,6 @@ import android.widget.ImageView
import android.widget.TextView import android.widget.TextView
import butterknife.BindView import butterknife.BindView
import butterknife.ButterKnife import butterknife.ButterKnife
import com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView
import de.kuschku.libquassel.util.helpers.nullIf import de.kuschku.libquassel.util.helpers.nullIf
import de.kuschku.quasseldroid.R import de.kuschku.quasseldroid.R
import de.kuschku.quasseldroid.settings.MessageSettings import de.kuschku.quasseldroid.settings.MessageSettings
...@@ -37,6 +36,7 @@ import de.kuschku.quasseldroid.util.helper.letIf ...@@ -37,6 +36,7 @@ import de.kuschku.quasseldroid.util.helper.letIf
import de.kuschku.quasseldroid.util.helper.loadAvatars import de.kuschku.quasseldroid.util.helper.loadAvatars
import de.kuschku.quasseldroid.util.helper.visibleIf import de.kuschku.quasseldroid.util.helper.visibleIf
import de.kuschku.quasseldroid.util.ui.SpanFormatter import de.kuschku.quasseldroid.util.ui.SpanFormatter
import de.kuschku.quasseldroid.util.ui.fastscroll.views.FastScrollRecyclerView
import de.kuschku.quasseldroid.viewmodel.data.IrcUserItem import de.kuschku.quasseldroid.viewmodel.data.IrcUserItem
class NickListAdapter( class NickListAdapter(
......
...@@ -227,7 +227,7 @@ class AboutFragment : DaggerFragment() { ...@@ -227,7 +227,7 @@ class AboutFragment : DaggerFragment() {
), ),
Library( Library(
name = "Kotlin Standard Library", name = "Kotlin Standard Library",
version = "1.2.50", version = "1.2.51",
license = apache2, license = apache2,
url = "https://kotlinlang.org/" url = "https://kotlinlang.org/"
), ),
......
...@@ -29,7 +29,7 @@ interface MatrixApi { ...@@ -29,7 +29,7 @@ interface MatrixApi {
@GET("/_matrix/client/r0/profile/{name}/avatar_url") @GET("/_matrix/client/r0/profile/{name}/avatar_url")
fun avatarUrl(@Path("name") name: String): Call<MatrixAvatarResponse> fun avatarUrl(@Path("name") name: String): Call<MatrixAvatarResponse>
@GET("/_matrix/media/r0/thumbnail/{server}/{id}/?width=32&height=32&method=crop") @GET("/_matrix/media/r0/thumbnail/{server}/{id}/?width={width}&height={height}&method={method]")
fun avatarThumbnail( fun avatarThumbnail(
@Path("server") server: String, @Path("server") server: String,
@Path("id") id: String, @Path("id") id: String,
......
...@@ -25,8 +25,8 @@ import android.graphics.Rect ...@@ -25,8 +25,8 @@ import android.graphics.Rect
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.support.v4.view.ViewCompat import android.support.v4.view.ViewCompat
import android.util.AttributeSet import android.util.AttributeSet
import com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView
import de.kuschku.quasseldroid.R import de.kuschku.quasseldroid.R
import de.kuschku.quasseldroid.util.ui.fastscroll.views.FastScrollRecyclerView
class DrawerRecyclerView @JvmOverloads constructor( class DrawerRecyclerView @JvmOverloads constructor(
context: Context, context: Context,
......
package de.kuschku.quasseldroid.util.ui.fastscroll.interfaces;
public interface OnFastScrollStateChangeListener {
/**
* Called when fast scrolling begins
*/
void onFastScrollStart();
/**
* Called when fast scrolling ends
*/
void onFastScrollStop();
}
/*
* Copyright (c) 2016 Tim Malseed
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.kuschku.quasseldroid.util.ui.fastscroll.utils;
import android.annotation.TargetApi;
import android.content.res.Resources;
import android.os.Build;
import android.util.TypedValue;
import android.view.View;
public class Utils {
/**
* Converts dp to px
*
* @param res Resources
* @param dp the value in dp
* @return int
*/
public static int toPixels(Resources res, float dp) {
return (int) (dp * res.getDisplayMetrics().density);
}
/**
* Converts sp to px
*
* @param res Resources
* @param sp the value in sp
* @return int
*/
public static int toScreenPixels(Resources res, float sp) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, res.getDisplayMetrics());
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
public static boolean isRtl(Resources res) {
return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) &&
(res.getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL);
}
}
/*
* Copyright (c) 2016 Tim Malseed
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.kuschku.quasseldroid.util.ui.fastscroll.views;
import android.animation.ObjectAnimator;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Typeface;
import android.support.annotation.Keep;
import android.text.TextUtils;
import de.kuschku.quasseldroid.util.ui.fastscroll.utils.Utils;
public class FastScrollPopup {
private FastScrollRecyclerView mRecyclerView;
private Resources mRes;
private int mBackgroundSize;
private int mCornerRadius;
private Path mBackgroundPath = new Path();
private RectF mBackgroundRect = new RectF();
private Paint mBackgroundPaint;
private int mBackgroundColor = 0xff000000;
private Rect mInvalidateRect = new Rect();
private Rect mTmpRect = new Rect();
// The absolute bounds of the fast scroller bg
private Rect mBgBounds = new Rect();
private String mSectionName;
private Paint mTextPaint;
private Rect mTextBounds = new Rect();
private float mAlpha = 1;
private ObjectAnimator mAlphaAnimator;
private boolean mVisible;
@FastScroller.FastScrollerPopupPosition
private int mPosition;
FastScrollPopup(Resources resources, FastScrollRecyclerView recyclerView) {
mRes = resources;
mRecyclerView = recyclerView;
mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mTextPaint.setAlpha(0);
setTextSize(Utils.toScreenPixels(mRes, 44));
setBackgroundSize(Utils.toPixels(mRes, 88));
}
public void setBgColor(int color) {
mBackgroundColor = color;
mBackgroundPaint.setColor(color);
mRecyclerView.invalidate(mBgBounds);
}
public void setTextColor(int color) {
mTextPaint.setColor(color);
mRecyclerView.invalidate(mBgBounds);
}
public void setTextSize(int size) {
mTextPaint.setTextSize(size);
mRecyclerView.invalidate(mBgBounds);
}
public void setBackgroundSize(int size) {
mBackgroundSize = size;
mCornerRadius = mBackgroundSize / 2;
mRecyclerView.invalidate(mBgBounds);
}
public void setTypeface(Typeface typeface) {
mTextPaint.setTypeface(typeface);
mRecyclerView.invalidate(mBgBounds);
}
/**
* Animates the visibility of the fast scroller popup.
*/
public void animateVisibility(boolean visible) {
if (mVisible != visible) {
mVisible = visible;
if (mAlphaAnimator != null) {
mAlphaAnimator.cancel();
}
mAlphaAnimator = ObjectAnimator.ofFloat(this, "alpha", visible ? 1f : 0f);
mAlphaAnimator.setDuration(visible ? 200 : 150);
mAlphaAnimator.start();
}
}
@Keep
public float getAlpha() {
return mAlpha;
}
// Setter/getter for the popup alpha for animations
@Keep
public void setAlpha(float alpha) {
mAlpha = alpha;
mRecyclerView.invalidate(mBgBounds);
}
@FastScroller.FastScrollerPopupPosition
public int getPopupPosition() {
return mPosition;
}
public void setPopupPosition(@FastScroller.FastScrollerPopupPosition int position) {
mPosition = position;
}
private float[] createRadii() {
if (mPosition == FastScroller.FastScrollerPopupPosition.CENTER) {
return new float[]{mCornerRadius, mCornerRadius, mCornerRadius, mCornerRadius, mCornerRadius, mCornerRadius, mCornerRadius, mCornerRadius};
}
if (Utils.isRtl(mRes)) {
return new float[]{mCornerRadius, mCornerRadius, mCornerRadius, mCornerRadius, mCornerRadius, mCornerRadius, 0, 0};
} else {
return new float[]{mCornerRadius, mCornerRadius, mCornerRadius, mCornerRadius, 0, 0, mCornerRadius, mCornerRadius};
}
}
public void draw(Canvas canvas) {
if (isVisible()) {
// Draw the fast scroller popup
int restoreCount = canvas.save();
canvas.translate(mBgBounds.left, mBgBounds.top);
mTmpRect.set(mBgBounds);
mTmpRect.offsetTo(0, 0);
mBackgroundPath.reset();
mBackgroundRect.set(mTmpRect);
float[] radii = createRadii();
mBackgroundPath.addRoundRect(mBackgroundRect, radii, Path.Direction.CW);
mBackgroundPaint.setAlpha((int) (Color.alpha(mBackgroundColor) * mAlpha));
mTextPaint.setAlpha((int) (mAlpha * 255));
canvas.drawPath(mBackgroundPath, mBackgroundPaint);
canvas.drawText(mSectionName, (mBgBounds.width() - mTextBounds.width()) / 2,
mBgBounds.height() - (mBgBounds.height() - mTextBounds.height()) / 2,
mTextPaint);
canvas.restoreToCount(restoreCount);
}
}
public void setSectionName(String sectionName) {
if (!sectionName.equals(mSectionName)) {
mSectionName = sectionName;
mTextPaint.getTextBounds(sectionName, 0, sectionName.length(), mTextBounds);
// Update the width to use measureText since that is more accurate
mTextBounds.right = (int) (mTextBounds.left + mTextPaint.measureText(sectionName));
}
}
/**
* Updates the bounds for the fast scroller.
*
* @return the invalidation rect for this update.
*/
public Rect updateFastScrollerBounds(FastScrollRecyclerView recyclerView, int thumbOffsetY) {
mInvalidateRect.set(mBgBounds);
mBgBounds.top += recyclerView.getPaddingTop();
mBgBounds.left += recyclerView.getPaddingLeft();
mBgBounds.right -= recyclerView.getPaddingRight();
mBgBounds.bottom -= recyclerView.getPaddingBottom();
if (isVisible()) {
// Calculate the dimensions and position of the fast scroller popup
int edgePadding = recyclerView.getScrollBarWidth();
int bgPadding = Math.round((mBackgroundSize - mTextBounds.height()) / 10) * 5;
int bgHeight = mBackgroundSize;
int bgWidth = Math.max(mBackgroundSize, mTextBounds.width() + (2 * bgPadding));
if (mPosition == FastScroller.FastScrollerPopupPosition.CENTER) {
mBgBounds.left = (recyclerView.getWidth() - bgWidth) / 2;
mBgBounds.right = mBgBounds.left + bgWidth;
mBgBounds.top = (recyclerView.getHeight() - bgHeight) / 2;
} else {
if (Utils.isRtl(mRes)) {
mBgBounds.left = (2 * recyclerView.getScrollBarWidth());
mBgBounds.right = mBgBounds.left + bgWidth;
} else {
mBgBounds.right = recyclerView.getWidth() - (2 * recyclerView.getScrollBarWidth());
mBgBounds.left = mBgBounds.right - bgWidth;
}
mBgBounds.top = recyclerView.getPaddingTop() + thumbOffsetY - bgHeight + recyclerView.getScrollBarThumbHeight() / 2;
mBgBounds.top = Math.max(edgePadding + recyclerView.getPaddingTop(), Math.min(mBgBounds.top, recyclerView.getHeight() - edgePadding - bgHeight - recyclerView.getPaddingBottom()));
}
mBgBounds.bottom = mBgBounds.top + bgHeight;
} else {
mBgBounds.setEmpty();
}
// Combine the old and new fast scroller bounds to create the full invalidate rect
mInvalidateRect.union(mBgBounds);
return mInvalidateRect;
}
public boolean isVisible() {
return (mAlpha > 0f) && (!TextUtils.isEmpty(mSectionName));
}
}
/*
* Copyright (c) 2016 Tim Malseed
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.kuschku.quasseldroid.util.ui.fastscroll.views;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Typeface;
import android.support.annotation.ColorInt;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.util.Log;
import android.util.SparseIntArray;
import android.view.MotionEvent;
import android.view.View;
import de.kuschku.quasseldroid.R;
import de.kuschku.quasseldroid.util.ui.fastscroll.interfaces.OnFastScrollStateChangeListener;
import de.kuschku.quasseldroid.util.ui.fastscroll.utils.Utils;
public class FastScrollRecyclerView extends RecyclerView implements RecyclerView.OnItemTouchListener {
private static final String TAG = "FastScrollRecyclerView";
private FastScroller mScrollbar;
private boolean mFastScrollEnabled = true;
private ScrollPositionState mScrollPosState = new ScrollPositionState();
private int mDownX;
private int mDownY;
private int mLastY;
private SparseIntArray mScrollOffsets;
private ScrollOffsetInvalidator mScrollOffsetInvalidator;
private OnFastScrollStateChangeListener mStateChangeListener;
public FastScrollRecyclerView(Context context) {
this(context, null);
}
public FastScrollRecyclerView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public FastScrollRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray typedArray = context.getTheme().obtainStyledAttributes(
attrs, R.styleable.FastScrollRecyclerView, 0, 0);
try {
mFastScrollEnabled = typedArray.getBoolean(R.styleable.FastScrollRecyclerView_fastScrollThumbEnabled, true);
} finally {
typedArray.recycle();
}
mScrollbar = new FastScroller(context, this, attrs);
mScrollOffsetInvalidator = new ScrollOffsetInvalidator();
mScrollOffsets = new SparseIntArray();
}
public int getScrollBarWidth() {
return mScrollbar.getWidth();
}
public int getScrollBarThumbHeight() {
return mScrollbar.getThumbHeight();
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
addOnItemTouchListener(this);
}
@Override
public void setAdapter(Adapter adapter) {
if (getAdapter() != null) {
getAdapter().unregisterAdapterDataObserver(mScrollOffsetInvalidator);
}
if (adapter != null) {
adapter.registerAdapterDataObserver(mScrollOffsetInvalidator);
}
super.setAdapter(adapter);
}
/**
* We intercept the touch handling only to support fast scrolling when initiated from the
* scroll bar. Otherwise, we fall back to the default RecyclerView touch handling.
*/
@Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent ev) {
return handleTouchEvent(ev);
}
@Override
public void onTouchEvent(RecyclerView rv, MotionEvent ev) {
handleTouchEvent(ev);
}
/**
* Handles the touch event and determines whether to show the fast scroller (or updates it if
* it is already showing).
*/
private boolean handleTouchEvent(MotionEvent ev) {
int action = ev.getAction();
int x = (int) ev.getX();
int y = (int) ev.getY();
switch (action) {
case MotionEvent.ACTION_DOWN:
// Keep track of the down positions
mDownX = x;
mDownY = mLastY = y;
mScrollbar.handleTouchEvent(ev, mDownX, mDownY, mLastY, mStateChangeListener);
break;
case MotionEvent.ACTION_MOVE:
mLastY = y;
mScrollbar.handleTouchEvent(ev, mDownX, mDownY, mLastY, mStateChangeListener);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
mScrollbar.handleTouchEvent(ev, mDownX, mDownY, mLastY, mStateChangeListener);
break;
}
return mScrollbar.isDragging();
}
@Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
}
/**
* Returns the available scroll height:
* AvailableScrollHeight = Total height of the all items - last page height
*
* @param yOffset the offset from the top of the recycler view to start tracking.
*/
protected int getAvailableScrollHeight(int adapterHeight, int yOffset) {
int visibleHeight = getHeight();
int scrollHeight = getPaddingTop() + yOffset + adapterHeight + getPaddingBottom();
int availableScrollHeight = scrollHeight - visibleHeight;
return availableScrollHeight;
}
/**
* Returns the available scroll bar height:
* AvailableScrollBarHeight = Total height of the visible view - thumb height
*/
protected int getAvailableScrollBarHeight() {
int visibleHeight = getHeight();
int availableScrollBarHeight = visibleHeight - mScrollbar.getThumbHeight();
return availableScrollBarHeight;
}
@Override
public void draw(Canvas c) {
super.draw(c);
if (mFastScrollEnabled) {
onUpdateScrollbar();
mScrollbar.draw(c);
}
}
/**
* Updates the scrollbar thumb offset to match the visible scroll of the recycler view. It does
* this by mapping the available scroll area of the recycler view to the available space for the
* scroll bar.
*
* @param scrollPosState the current scroll position
* @param rowCount the number of rows, used to calculate the total scroll height (assumes that
*/
protected void updateThumbPosition(ScrollPositionState scrollPosState, int rowCount) {
int availableScrollHeight;
int availableScrollBarHeight;
int scrolledPastHeight;
if (getAdapter() instanceof MeasurableAdapter) {
availableScrollHeight = getAvailableScrollHeight(calculateAdapterHeight(), 0);
scrolledPastHeight = calculateScrollDistanceToPosition(scrollPosState.rowIndex);
} else {
availableScrollHeight = getAvailableScrollHeight(rowCount * scrollPosState.rowHeight, 0);
scrolledPastHeight = scrollPosState.rowIndex * scrollPosState.rowHeight;
}
availableScrollBarHeight = getAvailableScrollBarHeight();
// Only show the scrollbar if there is height to be scrolled
if (availableScrollHeight <= 0) {
mScrollbar.setThumbPosition(-1, -1);
return;
}
// Calculate the current scroll position, the scrollY of the recycler view accounts for the
// view padding, while the scrollBarY is drawn right up to the background padding (ignoring
// padding)
int scrollY = getPaddingTop() + scrolledPastHeight - scrollPosState.rowTopOffset;
int scrollBarY = (int) (((float) scrollY / availableScrollHeight) * availableScrollBarHeight);
// Calculate the position and size of the scroll bar
int scrollBarX;
if (Utils.isRtl(getResources())) {
scrollBarX = 0;
} else {
scrollBarX = getWidth() - mScrollbar.getWidth();
}
mScrollbar.setThumbPosition(scrollBarX, scrollBarY);
}
/**
* Maps the touch (from 0..1) to the adapter position that should be visible.
*/
public String scrollToPositionAtProgress(float touchFraction) {
int itemCount = getAdapter().getItemCount();
if (itemCount == 0) {
return "";
}
int spanCount = 1;
int rowCount = itemCount;
if (getLayoutManager() instanceof GridLayoutManager) {
spanCount = ((GridLayoutManager) getLayoutManager()).getSpanCount();
rowCount = (int) Math.ceil((double) rowCount / spanCount);
}
// Stop the scroller if it is scrolling
stopScroll();
getCurScrollState(mScrollPosState);
float itemPos;
int availableScrollHeight;
int scrollPosition;
int scrollOffset;
if (getAdapter() instanceof MeasurableAdapter) {
itemPos = findItemPosition(touchFraction);
availableScrollHeight = calculateAdapterHeight();
scrollPosition = (int) itemPos;
scrollOffset = calculateScrollDistanceToPosition(scrollPosition) - (int) (touchFraction * availableScrollHeight);
} else {
itemPos = findItemPosition(touchFraction);
availableScrollHeight = getAvailableScrollHeight(rowCount * mScrollPosState.rowHeight, 0);
//The exact position of our desired item
int exactItemPos = (int) (availableScrollHeight * touchFraction);
//The offset used here is kind of hard to explain.
//If the position we wish to scroll to is, say, position 10.5, we scroll to position 10,
//and then offset by 0.5 * rowHeight. This is how we achieve smooth scrolling.
scrollPosition = spanCount * exactItemPos / mScrollPosState.rowHeight;
scrollOffset = -(exactItemPos % mScrollPosState.rowHeight);
}
LinearLayoutManager layoutManager = ((LinearLayoutManager) getLayoutManager());
layoutManager.scrollToPositionWithOffset(scrollPosition, scrollOffset);
if (!(getAdapter() instanceof SectionedAdapter)) {
return "";
}
int posInt = (int) ((touchFraction == 1) ? itemPos - 1 : itemPos);
SectionedAdapter sectionedAdapter = (SectionedAdapter) getAdapter();
return sectionedAdapter.getSectionName(posInt);
}
@SuppressWarnings("unchecked")
private float findItemPosition(float touchFraction) {
if (getAdapter() instanceof MeasurableAdapter) {
MeasurableAdapter measurer = (MeasurableAdapter) getAdapter();
int viewTop = (int) (touchFraction * calculateAdapterHeight());
for (int i = 0; i < getAdapter().getItemCount(); i++) {
int top = calculateScrollDistanceToPosition(i);
int bottom = top + measurer.getViewTypeHeight(this, findViewHolderForAdapterPosition(i), getAdapter().getItemViewType(i));
if (viewTop >= top && viewTop <= bottom) {
return i;
}
}
// Should never happen
Log.w(TAG, "Failed to find a view at the provided scroll fraction (" + touchFraction + ")");
return touchFraction * getAdapter().getItemCount();
} else {
return getAdapter().getItemCount() * touchFraction;
}
}
/**
* Updates the bounds for the scrollbar.
*/
public void onUpdateScrollbar() {
if (getAdapter() == null) {
return;
}
int rowCount = getAdapter().getItemCount();
if (getLayoutManager() instanceof GridLayoutManager) {
int spanCount = ((GridLayoutManager) getLayoutManager()).getSpanCount();
rowCount = (int) Math.ceil((double) rowCount / spanCount);
}
// Skip early if, there are no items.
if (rowCount == 0) {
mScrollbar.setThumbPosition(-1, -1);
return;
}
// Skip early if, there no child laid out in the container.
getCurScrollState(mScrollPosState);
if (mScrollPosState.rowIndex < 0) {
mScrollbar.setThumbPosition(-1, -1);
return;
}
updateThumbPosition(mScrollPosState, rowCount);
}
/**
* Returns the current scroll state of the apps rows.
*/
private void getCurScrollState(ScrollPositionState stateOut) {
stateOut.rowIndex = -1;
stateOut.rowTopOffset = -1;
stateOut.rowHeight = -1;
int itemCount = getAdapter().getItemCount();
// Return early if there are no items, or no children.
if (itemCount == 0 || getChildCount() == 0) {
return;
}
View child = getChildAt(0);
stateOut.rowIndex = getChildAdapterPosition(child);
if (getLayoutManager() instanceof GridLayoutManager) {
stateOut.rowIndex = stateOut.rowIndex / ((GridLayoutManager) getLayoutManager()).getSpanCount();
}
stateOut.rowTopOffset = getLayoutManager().getDecoratedTop(child);
stateOut.rowHeight = child.getHeight() + getLayoutManager().getTopDecorationHeight(child)
+ getLayoutManager().getBottomDecorationHeight(child);
}
/**
* Calculates the total height of all views above a position in the recycler view. This method
* should only be called when the attached adapter implements {@link MeasurableAdapter}.
*
* @param adapterIndex The index in the adapter to find the total height above the
* corresponding view
* @return The total height of all views above {@code adapterIndex} in pixels
*/
@SuppressWarnings("unchecked")
private int calculateScrollDistanceToPosition(int adapterIndex) {
if (!(getAdapter() instanceof MeasurableAdapter)) {
throw new IllegalStateException("calculateScrollDistanceToPosition() should only be called where the RecyclerView.Adapter is an instance of MeasurableAdapter");
}
if (mScrollOffsets.indexOfKey(adapterIndex) >= 0) {
return mScrollOffsets.get(adapterIndex);
}
int totalHeight = 0;
MeasurableAdapter measurer = (MeasurableAdapter) getAdapter();
// TODO Take grid layouts into account
for (int i = 0; i < adapterIndex; i++) {
mScrollOffsets.put(i, totalHeight);
int viewType = getAdapter().getItemViewType(i);
totalHeight += measurer.getViewTypeHeight(this, findViewHolderForAdapterPosition(i), viewType);
}
mScrollOffsets.put(adapterIndex, totalHeight);
return totalHeight;
}
/**
* Calculates the total height of the recycler view. This method should only be called when the
* attached adapter implements {@link MeasurableAdapter}.
*
* @return The total height of all rows in the RecyclerView
*/
private int calculateAdapterHeight() {
if (!(getAdapter() instanceof MeasurableAdapter)) {
throw new IllegalStateException("calculateAdapterHeight() should only be called where the RecyclerView.Adapter is an instance of MeasurableAdapter");
}
return calculateScrollDistanceToPosition(getAdapter().getItemCount());
}
public void showScrollbar() {
mScrollbar.show();
}
public void setThumbColor(@ColorInt int color) {
mScrollbar.setThumbColor(color);
}
public void setTrackColor(@ColorInt int color) {
mScrollbar.setTrackColor(color);
}
public void setPopupBgColor(@ColorInt int color) {
mScrollbar.setPopupBgColor(color);
}
public void setPopupTextColor(@ColorInt int color) {
mScrollbar.setPopupTextColor(color);
}
public void setPopupTextSize(int textSize) {
mScrollbar.setPopupTextSize(textSize);
}
public void setPopUpTypeface(Typeface typeface) {
mScrollbar.setPopupTypeface(typeface);
}
public void setAutoHideDelay(int hideDelay) {
mScrollbar.setAutoHideDelay(hideDelay);
}
public void setAutoHideEnabled(boolean autoHideEnabled) {
mScrollbar.setAutoHideEnabled(autoHideEnabled);
}
public void setOnFastScrollStateChangeListener(OnFastScrollStateChangeListener stateChangeListener) {
mStateChangeListener = stateChangeListener;
}
@Deprecated
public void setStateChangeListener(OnFastScrollStateChangeListener stateChangeListener) {
setOnFastScrollStateChangeListener(stateChangeListener);
}
public void setThumbInactiveColor(@ColorInt int color) {
mScrollbar.setThumbInactiveColor(color);
}
public void allowThumbInactiveColor(boolean allowInactiveColor) {
mScrollbar.enableThumbInactiveColor(allowInactiveColor);
}
@Deprecated
public void setThumbInactiveColor(boolean allowInactiveColor) {
allowThumbInactiveColor(allowInactiveColor);
}
public void setFastScrollEnabled(boolean fastScrollEnabled) {
mFastScrollEnabled = fastScrollEnabled;
}
@Deprecated
public void setThumbEnabled(boolean thumbEnabled) {
setFastScrollEnabled(thumbEnabled);
}
/**
* Set the FastScroll Popup position. This is either {@link FastScroller.FastScrollerPopupPosition#ADJACENT},
* meaning the popup moves adjacent to the FastScroll thumb, or {@link FastScroller.FastScrollerPopupPosition#CENTER},
* meaning the popup is static and centered within the RecyclerView.
*/
public void setPopupPosition(@FastScroller.FastScrollerPopupPosition int popupPosition) {
mScrollbar.setPopupPosition(popupPosition);
}
public interface SectionedAdapter {
@NonNull
String getSectionName(int position);
}
/**
* FastScrollRecyclerView by default assumes that all items in a RecyclerView will have
* ItemViews with the same heights so that the total height of all views in the RecyclerView
* can be calculated. If your list uses different view heights, then make your adapter implement
* this interface.
*/
public interface MeasurableAdapter<VH extends ViewHolder> {
/**
* Gets the height of a specific view type, including item decorations
*
* @param recyclerView The recyclerView that this item view will be placed in
* @param viewHolder The viewHolder that corresponds to this item view
* @param viewType The view type to get the height of
* @return The height of a single view for the given view type in pixels
*/
int getViewTypeHeight(RecyclerView recyclerView, @Nullable VH viewHolder, int viewType);
}
/**
* The current scroll state of the recycler view. We use this in onUpdateScrollbar()
* and scrollToPositionAtProgress() to determine the scroll position of the recycler view so
* that we can calculate what the scroll bar looks like, and where to jump to from the fast
* scroller.
*/
public static class ScrollPositionState {
// The index of the first visible row
int rowIndex;
// The offset of the first visible row
int rowTopOffset;
// The height of a given row (they are currently all the same height)
int rowHeight;
}
private class ScrollOffsetInvalidator extends AdapterDataObserver {
private void invalidateAllScrollOffsets() {
mScrollOffsets.clear();
}
@Override
public void onChanged() {
invalidateAllScrollOffsets();
}
@Override
public void onItemRangeChanged(int positionStart, int itemCount) {
invalidateAllScrollOffsets();
}
@Override
public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
invalidateAllScrollOffsets();
}
@Override
public void onItemRangeInserted(int positionStart, int itemCount) {
invalidateAllScrollOffsets();
}
@Override
public void onItemRangeRemoved(int positionStart, int itemCount) {
invalidateAllScrollOffsets();
}
@Override
public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
invalidateAllScrollOffsets();
}
}
}
/*
* Copyright (c) 2016 Tim Malseed
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.kuschku.quasseldroid.util.ui.fastscroll.views;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.support.annotation.ColorInt;
import android.support.annotation.IntDef;
import android.support.annotation.Keep;
import android.support.v4.view.animation.FastOutLinearInInterpolator;
import android.support.v4.view.animation.LinearOutSlowInInterpolator;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
import java.lang.annotation.Retention;
import de.kuschku.quasseldroid.R;
import de.kuschku.quasseldroid.util.ui.fastscroll.interfaces.OnFastScrollStateChangeListener;
import de.kuschku.quasseldroid.util.ui.fastscroll.utils.Utils;
import static java.lang.annotation.RetentionPolicy.SOURCE;
public class FastScroller {
private static final int DEFAULT_AUTO_HIDE_DELAY = 1500;
private final Runnable mHideRunnable;
private FastScrollRecyclerView mRecyclerView;
private FastScrollPopup mPopup;
private int mThumbHeight;
private int mWidth;
private Paint mThumb;
private Paint mTrack;
private Rect mTmpRect = new Rect();
private Rect mInvalidateRect = new Rect();
private Rect mInvalidateTmpRect = new Rect();
// The inset is the buffer around which a point will still register as a click on the scrollbar
private int mTouchInset;
// This is the offset from the top of the scrollbar when the user first starts touching. To
// prevent jumping, this offset is applied as the user scrolls.
private int mTouchOffset;
private Point mThumbPosition = new Point(-1, -1);
private Point mOffset = new Point(0, 0);
private boolean mIsDragging;
private Animator mAutoHideAnimator;
private boolean mAnimatingShow;
private int mAutoHideDelay = DEFAULT_AUTO_HIDE_DELAY;
private boolean mAutoHideEnabled = true;
private int mThumbActiveColor;
private int mThumbInactiveColor = 0x79000000;
private boolean mThumbInactiveState;
public FastScroller(Context context, FastScrollRecyclerView recyclerView, AttributeSet attrs) {
Resources resources = context.getResources();
mRecyclerView = recyclerView;
mPopup = new FastScrollPopup(resources, recyclerView);
mThumbHeight = Utils.toPixels(resources, 48);
mWidth = Utils.toPixels(resources, 8);
mTouchInset = Utils.toPixels(resources, -24);
mThumb = new Paint(Paint.ANTI_ALIAS_FLAG);
mTrack = new Paint(Paint.ANTI_ALIAS_FLAG);
TypedArray typedArray = context.getTheme().obtainStyledAttributes(
attrs, R.styleable.FastScrollRecyclerView, 0, 0);
try {
mAutoHideEnabled = typedArray.getBoolean(R.styleable.FastScrollRecyclerView_fastScrollAutoHide, true);
mAutoHideDelay = typedArray.getInteger(R.styleable.FastScrollRecyclerView_fastScrollAutoHideDelay, DEFAULT_AUTO_HIDE_DELAY);
mThumbInactiveState = typedArray.getBoolean(R.styleable.FastScrollRecyclerView_fastScrollEnableThumbInactiveColor, true);
mThumbActiveColor = typedArray.getColor(R.styleable.FastScrollRecyclerView_fastScrollThumbColor, 0x79000000);
mThumbInactiveColor = typedArray.getColor(R.styleable.FastScrollRecyclerView_fastScrollThumbInactiveColor, 0x79000000);
int trackColor = typedArray.getColor(R.styleable.FastScrollRecyclerView_fastScrollTrackColor, 0x28000000);
int popupBgColor = typedArray.getColor(R.styleable.FastScrollRecyclerView_fastScrollPopupBgColor, 0xff000000);
int popupTextColor = typedArray.getColor(R.styleable.FastScrollRecyclerView_fastScrollPopupTextColor, 0xffffffff);
int popupTextSize = typedArray.getDimensionPixelSize(R.styleable.FastScrollRecyclerView_fastScrollPopupTextSize, Utils.toScreenPixels(resources, 44));
int popupBackgroundSize = typedArray.getDimensionPixelSize(R.styleable.FastScrollRecyclerView_fastScrollPopupBackgroundSize, Utils.toPixels(resources, 88));
@FastScrollerPopupPosition int popupPosition = typedArray.getInteger(R.styleable.FastScrollRecyclerView_fastScrollPopupPosition, FastScrollerPopupPosition.ADJACENT);
mTrack.setColor(trackColor);
mThumb.setColor(mThumbInactiveState ? mThumbInactiveColor : mThumbActiveColor);
mPopup.setBgColor(popupBgColor);
mPopup.setTextColor(popupTextColor);
mPopup.setTextSize(popupTextSize);
mPopup.setBackgroundSize(popupBackgroundSize);
mPopup.setPopupPosition(popupPosition);
} finally {
typedArray.recycle();
}
mHideRunnable = new Runnable() {
@Override
public void run() {
if (!mIsDragging) {
if (mAutoHideAnimator != null) {
mAutoHideAnimator.cancel();
}
mAutoHideAnimator = ObjectAnimator.ofInt(FastScroller.this, "offsetX", (Utils.isRtl(mRecyclerView.getResources()) ? -1 : 1) * mWidth);
mAutoHideAnimator.setInterpolator(new FastOutLinearInInterpolator());
mAutoHideAnimator.setDuration(200);
mAutoHideAnimator.start();
}
}
};
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if (!mRecyclerView.isInEditMode()) {
show();
}
}
});
if (mAutoHideEnabled) {
postAutoHideDelayed();
}
}
public int getThumbHeight() {
return mThumbHeight;
}
public int getWidth() {
return mWidth;
}
public boolean isDragging() {
return mIsDragging;
}
/**
* Handles the touch event and determines whether to show the fast scroller (or updates it if
* it is already showing).
*/
public void handleTouchEvent(MotionEvent ev, int downX, int downY, int lastY,
OnFastScrollStateChangeListener stateChangeListener) {
ViewConfiguration config = ViewConfiguration.get(mRecyclerView.getContext());
int action = ev.getAction();
int y = (int) ev.getY();
switch (action) {
case MotionEvent.ACTION_DOWN:
if (isNearPoint(downX, downY)) {
mTouchOffset = downY - mThumbPosition.y;
}
break;
case MotionEvent.ACTION_MOVE:
// Check if we should start scrolling
if (!mIsDragging && isNearPoint(downX, downY) &&
Math.abs(y - downY) > config.getScaledTouchSlop()) {
mRecyclerView.getParent().requestDisallowInterceptTouchEvent(true);
mIsDragging = true;
mTouchOffset += (lastY - downY);
mPopup.animateVisibility(true);
if (stateChangeListener != null) {
stateChangeListener.onFastScrollStart();
}
if (mThumbInactiveState) {
mThumb.setColor(mThumbActiveColor);
}
}
if (mIsDragging) {
// Update the fastscroller section name at this touch position
int top = 0;
int bottom = mRecyclerView.getHeight() - mThumbHeight;
float boundedY = (float) Math.max(top, Math.min(bottom, y - mTouchOffset));
String sectionName = mRecyclerView.scrollToPositionAtProgress((boundedY - top) / (bottom - top));
mPopup.setSectionName(sectionName);
mPopup.animateVisibility(!sectionName.isEmpty());
mRecyclerView.invalidate(mPopup.updateFastScrollerBounds(mRecyclerView, mThumbPosition.y));
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
mTouchOffset = 0;
if (mIsDragging) {
mIsDragging = false;
mPopup.animateVisibility(false);
if (stateChangeListener != null) {
stateChangeListener.onFastScrollStop();
}
}
if (mThumbInactiveState) {
mThumb.setColor(mThumbInactiveColor);
}
break;
}
}
public void draw(Canvas canvas) {
if (mThumbPosition.x < 0 || mThumbPosition.y < 0) {
return;
}
//Background
canvas.drawRect(
mThumbPosition.x + mOffset.x,
mOffset.y,
mWidth + mThumbPosition.x + mOffset.x,
mRecyclerView.getHeight() + mOffset.y,
mTrack
);
//Handle
canvas.drawRect(
mThumbPosition.x + mOffset.x,
mThumbPosition.y + mOffset.y,
mWidth + mThumbPosition.x + mOffset.x,
mThumbHeight + mThumbPosition.y + mOffset.y,
mThumb
);
//Popup
mPopup.draw(canvas);
}
/**
* Returns whether the specified points are near the scroll bar bounds.
*/
private boolean isNearPoint(int x, int y) {
mTmpRect.set(mThumbPosition.x, mThumbPosition.y, mThumbPosition.x + mWidth,
mThumbPosition.y + mThumbHeight);
mTmpRect.inset(mTouchInset, mTouchInset);
return mTmpRect.contains(x, y);
}
public void setThumbPosition(int x, int y) {
if (mThumbPosition.x == x && mThumbPosition.y == y) {
return;
}
// do not create new objects here, this is called quite often
mInvalidateRect.set(mRecyclerView.getPaddingLeft() + mThumbPosition.x + mOffset.x, mRecyclerView.getPaddingTop() + mOffset.y, mThumbPosition.x + mOffset.x + mWidth - mRecyclerView.getPaddingRight(), mRecyclerView.getPaddingTop() + mRecyclerView.getHeight() - mRecyclerView.getPaddingBottom() + mOffset.y);
mThumbPosition.set(
x * (mRecyclerView.getWidth() - mRecyclerView.getPaddingLeft() - mRecyclerView.getPaddingLeft()) / mRecyclerView.getWidth() + mRecyclerView.getPaddingLeft(),
y * (mRecyclerView.getHeight() - mRecyclerView.getPaddingTop() - mRecyclerView.getPaddingBottom()) / mRecyclerView.getHeight() + mRecyclerView.getPaddingTop()
);
mInvalidateTmpRect.set(mRecyclerView.getPaddingLeft() + mThumbPosition.x + mOffset.x, mRecyclerView.getPaddingTop() + mOffset.y, mThumbPosition.x + mOffset.x + mWidth - mRecyclerView.getPaddingRight(), mRecyclerView.getPaddingTop() + mRecyclerView.getHeight() - mRecyclerView.getPaddingBottom() + mOffset.y);
mInvalidateRect.union(mInvalidateTmpRect);
mRecyclerView.invalidate(mInvalidateRect);
}
public void setOffset(int x, int y) {
if (mOffset.x == x && mOffset.y == y) {
return;
}
// do not create new objects here, this is called quite often
mInvalidateRect.set(mRecyclerView.getPaddingLeft() + mThumbPosition.x + mOffset.x, mRecyclerView.getPaddingTop() + mOffset.y, mThumbPosition.x + mOffset.x + mWidth - mRecyclerView.getPaddingRight(), mRecyclerView.getPaddingTop() + mRecyclerView.getHeight() - mRecyclerView.getPaddingBottom() + mOffset.y);
mOffset.set(x, y);
mInvalidateTmpRect.set(mRecyclerView.getPaddingLeft() + mThumbPosition.x + mOffset.x, mRecyclerView.getPaddingTop() + mOffset.y, mThumbPosition.x + mOffset.x + mWidth - mRecyclerView.getPaddingRight(), mRecyclerView.getPaddingTop() + mRecyclerView.getHeight() - mRecyclerView.getPaddingBottom() + mOffset.y);
mInvalidateRect.union(mInvalidateTmpRect);
mRecyclerView.invalidate(mInvalidateRect);
}
@Keep
public int getOffsetX() {
return mOffset.x;
}
// Setter/getter for the popup alpha for animations
@Keep
public void setOffsetX(int x) {
setOffset(x, mOffset.y);
}
public void show() {
if (!mAnimatingShow) {
if (mAutoHideAnimator != null) {
mAutoHideAnimator.cancel();
}
mAutoHideAnimator = ObjectAnimator.ofInt(this, "offsetX", 0);
mAutoHideAnimator.setInterpolator(new LinearOutSlowInInterpolator());
mAutoHideAnimator.setDuration(150);
mAutoHideAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationCancel(Animator animation) {
super.onAnimationCancel(animation);
mAnimatingShow = false;
}
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
mAnimatingShow = false;
}
});
mAnimatingShow = true;
mAutoHideAnimator.start();
}
if (mAutoHideEnabled) {
postAutoHideDelayed();
} else {
cancelAutoHide();
}
}
protected void postAutoHideDelayed() {
if (mRecyclerView != null) {
cancelAutoHide();
mRecyclerView.postDelayed(mHideRunnable, mAutoHideDelay);
}
}
protected void cancelAutoHide() {
if (mRecyclerView != null) {
mRecyclerView.removeCallbacks(mHideRunnable);
}
}
public void setThumbColor(@ColorInt int color) {
mThumb.setColor(color);
mRecyclerView.invalidate(mInvalidateRect);
}
public void setTrackColor(@ColorInt int color) {
mTrack.setColor(color);
mRecyclerView.invalidate(mInvalidateRect);
}
public void setPopupBgColor(@ColorInt int color) {
mPopup.setBgColor(color);
}
public void setPopupTextColor(@ColorInt int color) {
mPopup.setTextColor(color);
}
public void setPopupTypeface(Typeface typeface) {
mPopup.setTypeface(typeface);
}
public void setPopupTextSize(int size) {
mPopup.setTextSize(size);
}
public void setAutoHideDelay(int hideDelay) {
mAutoHideDelay = hideDelay;
if (mAutoHideEnabled) {
postAutoHideDelayed();
}
}
public void setAutoHideEnabled(boolean autoHideEnabled) {
mAutoHideEnabled = autoHideEnabled;
if (autoHideEnabled) {
postAutoHideDelayed();
} else {
cancelAutoHide();
}
}
public void setPopupPosition(@FastScrollerPopupPosition int popupPosition) {
mPopup.setPopupPosition(popupPosition);
}
public void setThumbInactiveColor(@ColorInt int color) {
mThumbInactiveColor = color;
enableThumbInactiveColor(true);
}
public void enableThumbInactiveColor(boolean enableInactiveColor) {
mThumbInactiveState = enableInactiveColor;
mThumb.setColor(mThumbInactiveState ? mThumbInactiveColor : mThumbActiveColor);
}
@Deprecated
public void setThumbInactiveColor(boolean thumbInactiveColor) {
enableThumbInactiveColor(thumbInactiveColor);
}
@Retention(SOURCE)
@IntDef({FastScrollerPopupPosition.ADJACENT, FastScrollerPopupPosition.CENTER})
public @interface FastScrollerPopupPosition {
int ADJACENT = 0;
int CENTER = 1;
}
}
...@@ -49,7 +49,7 @@ ...@@ -49,7 +49,7 @@
</android.support.design.widget.AppBarLayout> </android.support.design.widget.AppBarLayout>
<com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView <de.kuschku.quasseldroid.util.ui.fastscroll.views.FastScrollRecyclerView
android:id="@+id/chatList" android:id="@+id/chatList"
style="@style/Widget.FastScroller" style="@style/Widget.FastScroller"
android:layout_width="match_parent" android:layout_width="match_parent"
......
...@@ -88,4 +88,52 @@ ...@@ -88,4 +88,52 @@
<!-- InsetLayouts --> <!-- InsetLayouts -->
<attr name="insetBackground" format="color|reference" /> <attr name="insetBackground" format="color|reference" />
<!-- DrawerRecyclerView -->
<declare-styleable name="DrawerRecyclerView">
<attr name="insetBackground" />
</declare-styleable>
<!-- ShadowView -->
<declare-styleable name="ShadowView">
<attr name="android:gravity" />
</declare-styleable>
<!-- RingtonePreference -->
<declare-styleable name="RingtonePreference">
<!-- Which ringtone type(s) to show in the picker. -->
<attr name="ringtoneType">
<!-- Ringtones. -->
<flag name="ringtone" value="1" />
<!-- Notification sounds. -->
<flag name="notification" value="2" />
<!-- Alarm sounds. -->
<flag name="alarm" value="4" />
<!-- All available ringtone sounds. -->
<flag name="all" value="7" />
</attr>
<!-- Whether to show an item for a default sound. -->
<attr name="showDefault" format="boolean" />
<!-- Whether to show an item for 'Silent'. -->
<attr name="showSilent" format="boolean" />
</declare-styleable>
<!-- FastScroll RecyclerView -->
<declare-styleable name="FastScrollRecyclerView">
<attr name="fastScrollThumbColor" format="reference|color" />
<attr name="fastScrollThumbInactiveColor" format="reference|color" />
<attr name="fastScrollTrackColor" format="reference|color" />
<attr name="fastScrollPopupBgColor" format="reference|color" />
<attr name="fastScrollPopupTextColor" format="reference|color" />
<attr name="fastScrollPopupTextSize" format="reference|dimension" />
<attr name="fastScrollPopupBackgroundSize" format="reference|dimension" />
<attr name="fastScrollPopupPosition" format="enum">
<enum name="adjacent" value="0" />
<enum name="center" value="1" />
</attr>
<attr name="fastScrollAutoHide" format="reference|boolean" />
<attr name="fastScrollAutoHideDelay" format="reference|integer" />
<attr name="fastScrollEnableThumbInactiveColor" format="reference|boolean" />
<attr name="fastScrollThumbEnabled" format="reference|boolean" />
</declare-styleable>
</resources> </resources>
...@@ -335,33 +335,4 @@ ...@@ -335,33 +335,4 @@
<style name="Widget.NavigationDrawerLayout" parent=""> <style name="Widget.NavigationDrawerLayout" parent="">
<item name="insetBackground">#4000</item> <item name="insetBackground">#4000</item>
</style> </style>
<!-- DrawerRecyclerView -->
<declare-styleable name="DrawerRecyclerView">
<attr name="insetBackground" />
</declare-styleable>
<!-- ShadowView -->
<declare-styleable name="ShadowView">
<attr name="android:gravity" />
</declare-styleable>
<!-- RingtonePreference -->
<declare-styleable name="RingtonePreference">
<!-- Which ringtone type(s) to show in the picker. -->
<attr name="ringtoneType">
<!-- Ringtones. -->
<flag name="ringtone" value="1" />
<!-- Notification sounds. -->
<flag name="notification" value="2" />
<!-- Alarm sounds. -->
<flag name="alarm" value="4" />
<!-- All available ringtone sounds. -->
<flag name="all" value="7" />
</attr>
<!-- Whether to show an item for a default sound. -->
<attr name="showDefault" format="boolean" />
<!-- Whether to show an item for 'Silent'. -->
<attr name="showSilent" format="boolean" />
</declare-styleable>
</resources> </resources>
...@@ -24,7 +24,7 @@ buildscript { ...@@ -24,7 +24,7 @@ buildscript {
} }
dependencies { dependencies {
classpath("com.android.tools.build:gradle:3.1.3") classpath("com.android.tools.build:gradle:3.1.3")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.2.50") classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.2.51")
} }
} }
......
...@@ -29,7 +29,7 @@ plugins { ...@@ -29,7 +29,7 @@ plugins {
} }
dependencies { dependencies {
implementation(kotlin("stdlib", "1.2.50")) implementation(kotlin("stdlib", "1.2.51"))
withVersion("27.1.1") { withVersion("27.1.1") {
implementation("com.android.support", "support-annotations", version) implementation("com.android.support", "support-annotations", version)
......
...@@ -27,7 +27,7 @@ plugins { ...@@ -27,7 +27,7 @@ plugins {
} }
dependencies { dependencies {
implementation(kotlin("stdlib", "1.2.50")) implementation(kotlin("stdlib", "1.2.51"))
withVersion("27.1.1") { withVersion("27.1.1") {
implementation("com.android.support", "support-annotations", version) implementation("com.android.support", "support-annotations", version)
......
...@@ -45,7 +45,7 @@ android { ...@@ -45,7 +45,7 @@ android {
} }
dependencies { dependencies {
implementation(kotlin("stdlib", "1.2.50")) implementation(kotlin("stdlib", "1.2.51"))
implementation("com.google.code.gson", "gson", "2.8.2") implementation("com.google.code.gson", "gson", "2.8.2")
} }
...@@ -52,7 +52,7 @@ android { ...@@ -52,7 +52,7 @@ android {
} }
dependencies { dependencies {
implementation(kotlin("stdlib", "1.2.50")) implementation(kotlin("stdlib", "1.2.51"))
// App Compat // App Compat
withVersion("27.1.1") { withVersion("27.1.1") {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment