/* * Copyright © 2016-2017, Turing Technologies, an unincorporated organisation of Wynne Plaga * * 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 com.turingtechnologies.materialscrollbar; import android.content.Context; import android.graphics.Typeface; import android.graphics.drawable.GradientDrawable; import android.support.annotation.ColorInt; import android.support.v4.content.ContextCompat; import android.support.v4.view.ViewCompat; import android.support.v7.widget.RecyclerView; import android.util.Log; import android.util.TypedValue; import android.view.ViewGroup; import android.widget.RelativeLayout; import android.widget.TextView; /** * Devs should not normally need to extend this class. Just use {@link CustomIndicator} instead. * However, this is public to leave the option open. * * T is the interface needed in the corresponding {@link RecyclerView.Adapter}. * U is the sub-class of indicator. * The second parameter for the constructor is the class of T. */ @SuppressWarnings("unchecked") public abstract class Indicator<T, U extends Indicator> extends RelativeLayout{ protected TextView textView; protected Context context; private boolean addSpace; private MaterialScrollBar materialScrollBar; private boolean rtl; private int size; private Class<T> adapterClass; public Indicator(Context context, Class<T> adapter) { super(context); this.context = context; textView = new TextView(context); setVisibility(INVISIBLE); adapterClass = adapter; } void setSizeCustom(int size){ if(addSpace){ this.size = size + Utils.getDP(10, this); } else { this.size = size; } setLayoutParams(refreshMargins((LayoutParams) getLayoutParams())); } void setRTL(boolean rtl){ this.rtl = rtl; } void linkToScrollBar(MaterialScrollBar msb, boolean addSpace){ this.addSpace = addSpace; materialScrollBar = msb; if(addSpace){ size = Utils.getDP(15, this) + materialScrollBar.handleThumb.getWidth(); } else { size = Utils.getDP(2, this) + materialScrollBar.handleThumb.getWidth(); } ViewCompat.setBackground(this, ContextCompat.getDrawable(context, rtl ? R.drawable.indicator_ltr : R.drawable.indicator)); LayoutParams lp = new LayoutParams(Utils.getDP(getIndicatorWidth(), this), Utils.getDP(getIndicatorHeight(), this)); lp = refreshMargins(lp); textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, getTextSize()); LayoutParams tvlp = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); tvlp.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE); addView(textView, tvlp); ((GradientDrawable)getBackground()).setColor(msb.handleColour); if (rtl) { lp.addRule(ALIGN_LEFT, msb.getId()); } else { lp.addRule(ALIGN_RIGHT, msb.getId()); } ((ViewGroup)msb.getParent()).addView(this, lp); } LayoutParams refreshMargins(LayoutParams lp){ if(rtl) { lp.setMargins(size, 0, 0, 0); } else { lp.setMargins(0, 0, size, 0); } return lp; } /** * Used by the materialScrollBar to move the indicator with the handleThumb * @param y Position to which the indicator should move. */ void setScroll(float y){ if(getVisibility() == VISIBLE){ y -= 75 - materialScrollBar.getIndicatorOffset() + Utils.getDP(getIndicatorHeight() / 2, this); if(y < 5){ y = 5; } ViewCompat.setY(this, y); } } /** * Sets the content text for the indicator and resizes if needed */ void setText(int section){ String newText; try{ T adapter = (T) materialScrollBar.recyclerView.getAdapter(); if (adapter == null) { Log.e("MaterialScrollBarLib", "The adapter for your recyclerView has not been set; " + "skipping indicator layout."); return; } newText = getTextElement(section, adapter); } catch (ArrayIndexOutOfBoundsException e){ newText = "Error"; } if (!textView.getText().equals(newText)){ textView.setText(newText); LayoutWrapContentUpdater.wrapContentAgain(this); } } /** * This method tests the adapter to make sure that it implements the needed interface. * * @param adapter The adapter of the attached {@link RecyclerView}. */ void testAdapter(RecyclerView.Adapter adapter) { if (adapter == null) { Log.e("MaterialScrollBarLib", "The adapter for your recyclerView has not been set; " + "skipping indicator layout."); return; } if(!adapterClass.isInstance(adapter)){ throw new IllegalArgumentException( "In order to add this indicator, the adapter for your recyclerView, " + adapter.getClass().getName() + ", MUST implement " + Utils.getGenericName(this) + "."); } } public U setTypeface(Typeface typeface){ textView.setTypeface(typeface); return (U)this; } /** * Used by the materialScrollBar to change the text colour for the indicator. * @param colour The desired text colour. */ void setTextColour(@ColorInt int colour){ textView.setTextColor(colour); } /** * @param currentSection The section that the indicator is indicating for. * @param adapter The adapter of the attached {@link RecyclerView}. * @return The text that should go in the indicator. */ protected abstract String getTextElement(Integer currentSection, T adapter); /** * @return The height of the indicator in px. If it is variable return any value and resize * the view yourself. */ protected abstract int getIndicatorHeight(); /** * @return The width of the indicator in px. If it is variable return any value and resize * the view yourself. */ protected abstract int getIndicatorWidth(); /** * @return The size of text in the indicator. */ protected abstract int getTextSize(); }