Java tutorial
/* * The MIT License (MIT) * * Copyright (c) 2015 Omada Health, Inc * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.github.omadahealth.slidepager.lib.views; import android.content.Context; import android.content.res.TypedArray; import android.databinding.DataBindingUtil; import android.graphics.Typeface; import android.support.v4.view.ViewPager; import android.view.LayoutInflater; import android.view.View; import android.widget.ImageView; import com.daimajia.androidanimations.library.Techniques; import com.daimajia.androidanimations.library.YoYo; import com.github.omadahealth.slidepager.lib.R; import com.github.omadahealth.slidepager.lib.SlideTransformer; import com.github.omadahealth.slidepager.lib.databinding.ViewChartSlideBinding; import com.github.omadahealth.slidepager.lib.interfaces.OnSlideListener; import com.github.omadahealth.slidepager.lib.interfaces.OnSlidePageChangeListener; import com.github.omadahealth.slidepager.lib.utils.ChartProgressAttr; import com.github.omadahealth.slidepager.lib.utils.ProgressAttr; import com.github.omadahealth.slidepager.lib.utils.Utilities; import com.github.omadahealth.typefaceview.TypefaceTextView; import com.github.omadahealth.typefaceview.TypefaceType; import java.util.ArrayList; import java.util.List; /** * Created by stoyan on 4/7/15. */ public class SlideChartView extends AbstractSlideView { /** * The tag for logging */ private static final String TAG = "SlideBarChartView"; /** * The duration for the animation for {@link #mSelectedView} when {@link OnSlideListener#onDaySelected(int, int)} * is not allowed */ private static final long NOT_ALLOWED_SHAKE_ANIMATION_DURATION = 500; /** * An array that holds all the {@link TypefaceTextView} that are at the top */ private List<TypefaceTextView> mProgressTopTextList = new ArrayList<>(7); /** * An array that holds all the {@link ProgressView} for this layout */ private List<ProgressView> mProgressList = new ArrayList<>(7); /** * An array that holds all the {@link TypefaceTextView} that are at the bottom */ private List<TypefaceTextView> mProgressBottomTextList = new ArrayList<>(7); /** * An array that holds all the {@link android.widget.ImageView} that are part of the chart bar */ private List<ImageView> mChartBarList = new ArrayList<>(15); /** * The callback listener for when views are clicked */ private OnSlideListener mCallback; /** * The list of {@link ProgressAttr} to associate with {@link #mProgressList}. * Used in {@link #injectViewsAndAttributes()} */ private List<ChartProgressAttr> mProgressAttr; /** * The int tag of the selected {@link ProgressView} we store so that we can restore the previous selected day. */ private static int mSelectedView; /** * The default animation time */ private static final int DEFAULT_PROGRESS_ANIMATION_TIME = 1000; /** * True of we want to shake the view in {@link #toggleSelectedViews(int)} */ private boolean mShakeIfNotSelectable; /** * The xml attributes of this view */ private TypedArray mAttributes; /** * Indicates if the user configured the style to be reanimating each time we are scrolling the {@link com.github.omadahealth.slidepager.lib.SlidePager} * or not. */ private boolean mHasToReanimate; /** * The {@link android.graphics.Color} link of the special day: {@link ProgressAttr#isSpecial()} */ private int mSpecialBottomTextColor; /** * The {@link android.graphics.Color} link of the top texts: {@link SlideChartView#mProgressTopTextList} */ private int mTopTextColor; /** * The {@link android.graphics.Color} link of the Bottom texts: {@link SlideChartView#mProgressBottomTextList} */ private int mBottomTextColor; /** * The {@link android.graphics.Color} link of the Bar: {@link SlideChartView#mChartBarList} */ private int mChartBarColor; /** * The {@link android.support.annotation.DimenRes} link of the Bar: {@link SlideChartView#mChartBarList} */ private float mChartBarSize; /** * The position of this page within the {@link com.github.omadahealth.slidepager.lib.SlidePager} */ private int mPagePosition; /** * The animation time in milliseconds that we animate the progress */ private int mProgressAnimationTime = DEFAULT_PROGRESS_ANIMATION_TIME; /** * The outline size set to the {@link ProgressView} and used here to set padding to the lines * so that they don't overlap in {@link #setPaddingForOutlineSize(View)} */ private float mOutlineSize; /** * A user defined {@link ViewPager.OnPageChangeListener} */ private OnSlidePageChangeListener<ChartProgressAttr> mUserPageListener; /** * The binding object created in {@link #init(Context, TypedArray, int, OnSlidePageChangeListener)} */ private ViewChartSlideBinding mBinding; public SlideChartView(Context context) { this(context, null, -1, null); } public SlideChartView(Context context, TypedArray attributes, int pagePosition, OnSlidePageChangeListener pageListener) { super(context, null); init(context, attributes, pagePosition, pageListener); } private void loadStyledAttributes(TypedArray attributes) { mAttributes = attributes; if (mAttributes != null) { mOutlineSize = mAttributes.getDimension( R.styleable.SlidePager_slide_progress_not_completed_outline_size, getResources().getDimension(R.dimen.circular_bar_default_outline_width)); mHasToReanimate = mAttributes.getBoolean(R.styleable.SlidePager_slide_pager_reanimate_slide_view, true); mTopTextColor = attributes.getColor(R.styleable.SlidePager_slide_progress_chart_bar_top_text_color, getResources().getColor(R.color.default_progress_chart_bar_top_text)); mSpecialBottomTextColor = attributes.getColor( R.styleable.SlidePager_slide_progress_chart_bar_bottom_special_text_color, getResources().getColor(R.color.default_progress_chart_bar_special_bottom_text)); mBottomTextColor = attributes.getColor( R.styleable.SlidePager_slide_progress_chart_bar_bottom_text_color, getResources().getColor(R.color.default_progress_chart_bar_bottom_text)); mChartBarColor = attributes.getColor(R.styleable.SlidePager_slide_progress_chart_color, getResources().getColor(R.color.default_progress_chart_bar_color)); mChartBarSize = attributes.getDimension(R.styleable.SlidePager_slide_progress_chart_bar_size, getResources().getDimension(R.dimen.default_progress_chart_bar_size)); mShakeIfNotSelectable = attributes.getBoolean(R.styleable.SlidePager_slide_shake_if_not_selectable, true); } } /** * Bind the views, set the listeners and attrs * * @param context */ private void init(Context context, TypedArray attributes, int pagePosition, OnSlidePageChangeListener pageListener) { if (!isInEditMode()) { this.mPagePosition = pagePosition; this.mUserPageListener = pageListener; LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); mBinding = DataBindingUtil.inflate(inflater, R.layout.view_chart_slide, this, true); mAttributes = attributes; loadStyledAttributes(attributes); injectViewsAndAttributes(); setListeners(); } } /** * Inject the views into {@link #mProgressList} */ private void injectViewsAndAttributes() { if (mUserPageListener == null) { return; } //Init the ProgressAttr for this page mProgressAttr = new ArrayList<>(); for (int i = 0; i < 7; i++) { mProgressAttr.add(mUserPageListener.getDayProgress(mPagePosition, i)); } //Progress top texts mProgressTopTextList.add(mBinding.progressTopText1); mProgressTopTextList.add(mBinding.progressTopText2); mProgressTopTextList.add(mBinding.progressTopText3); mProgressTopTextList.add(mBinding.progressTopText4); mProgressTopTextList.add(mBinding.progressTopText5); mProgressTopTextList.add(mBinding.progressTopText6); mProgressTopTextList.add(mBinding.progressTopText7); //Progress circles mProgressList.add(mBinding.progress1.loadStyledAttributes(mAttributes, mProgressAttr.get(0))); mProgressList.add(mBinding.progress2.loadStyledAttributes(mAttributes, mProgressAttr.get(1))); mProgressList.add(mBinding.progress3.loadStyledAttributes(mAttributes, mProgressAttr.get(2))); mProgressList.add(mBinding.progress4.loadStyledAttributes(mAttributes, mProgressAttr.get(3))); mProgressList.add(mBinding.progress5.loadStyledAttributes(mAttributes, mProgressAttr.get(4))); mProgressList.add(mBinding.progress6.loadStyledAttributes(mAttributes, mProgressAttr.get(5))); mProgressList.add(mBinding.progress7.loadStyledAttributes(mAttributes, mProgressAttr.get(6))); //Progress bottom texts mProgressBottomTextList.add(mBinding.progressBottomText1); mProgressBottomTextList.add(mBinding.progressBottomText2); mProgressBottomTextList.add(mBinding.progressBottomText3); mProgressBottomTextList.add(mBinding.progressBottomText4); mProgressBottomTextList.add(mBinding.progressBottomText5); mProgressBottomTextList.add(mBinding.progressBottomText6); mProgressBottomTextList.add(mBinding.progressBottomText7); //Chart bars mChartBarList.add(mBinding.progress1BarTop); mChartBarList.add(mBinding.progress2BarTop); mChartBarList.add(mBinding.progress3BarTop); mChartBarList.add(mBinding.progress4BarTop); mChartBarList.add(mBinding.progress5BarTop); mChartBarList.add(mBinding.progress6BarTop); mChartBarList.add(mBinding.progress7BarTop); mChartBarList.add(mBinding.progress1BarBottom); mChartBarList.add(mBinding.progress2BarBottom); mChartBarList.add(mBinding.progress3BarBottom); mChartBarList.add(mBinding.progress4BarBottom); mChartBarList.add(mBinding.progress5BarBottom); mChartBarList.add(mBinding.progress6BarBottom); mChartBarList.add(mBinding.progress7BarBottom); mChartBarList.add(mBinding.progressBottomAxis); //Set padding depending on outline size setPaddingForOutlineSize(mBinding.progress1); setPaddingForOutlineSize(mBinding.progress2); setPaddingForOutlineSize(mBinding.progress3); setPaddingForOutlineSize(mBinding.progress4); setPaddingForOutlineSize(mBinding.progress5); setPaddingForOutlineSize(mBinding.progress6); setPaddingForOutlineSize(mBinding.progress7); //Init the tags of the subviews SlideTransformer.initTags(this); //Init values and days text initTopAndBottomTexts(); //Init bar colors and sizes initBarColorsAndSize(); } /** * Set the padding on the {@link View} based on {@link #mOutlineSize} */ private void setPaddingForOutlineSize(View view) { view.setPadding(view.getPaddingLeft(), (int) mOutlineSize, view.getPaddingRight(), (int) mOutlineSize); } /** * Init the list of {@link TypefaceTextView}: {@link #mProgressTopTextList} and {@link #mProgressBottomTextList} */ private void initTopAndBottomTexts() { //Set the top text to be the values for (int i = 0; i < mProgressTopTextList.size(); i++) { if (mProgressAttr != null && mProgressAttr.get(i) != null) { String oneDecimal = Utilities.formatWeight(mProgressAttr.get(i).getValue()); mProgressTopTextList.get(i).setText(oneDecimal); mProgressTopTextList.get(i).setTextColor(mTopTextColor); } } //Set the bottom texts to be the day values and set the color if special for (int i = 0; i < mProgressBottomTextList.size(); i++) { if (mProgressAttr != null && mProgressAttr.get(i) != null) { TypefaceTextView currentTextView = mProgressBottomTextList.get(i); currentTextView.setTextColor( mProgressAttr.get(i).isSpecial() ? mSpecialBottomTextColor : mBottomTextColor); currentTextView.setText(mProgressAttr.get(i).getBottomText()); } } } /** * Init the {@link android.graphics.Color} of the {@link #mChartBarList} */ private void initBarColorsAndSize() { for (ImageView imageView : mChartBarList) { imageView.setBackgroundColor(mChartBarColor); if (imageView.getId() != R.id.progress_bottom_axis) { imageView.setMinimumHeight((int) (mChartBarSize / 2.0f)); } } } /** * Set up listeners for all the views in {@link #mProgressList} */ private void setListeners() { for (final ProgressView progressView : mProgressList) { if (progressView != null) { progressView.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { int index = progressView.getIntTag(); boolean allowed = true; if (mCallback != null) { allowed = mCallback.isDaySelectable(mPagePosition, index); } if (allowed) { if (mCallback != null) { mCallback.onDaySelected(mPagePosition, index); } toggleSelectedViews(index); } else { if (mShakeIfNotSelectable) { YoYo.with(Techniques.Shake).duration(NOT_ALLOWED_SHAKE_ANIMATION_DURATION) .playOn(SlideChartView.this); } } } }); } } } /** * Sets the selected {@link ProgressView} * * @param selected The index of the selected view in {@link #mProgressList} */ private void toggleSelectedViews(int selected) { mSelectedView = selected; for (int i = 0; i < mProgressList.size(); i++) { ProgressView day = mProgressList.get(i); TypefaceTextView currentBottomText = mProgressBottomTextList.get(i); if (day.getIntTag() == mSelectedView) { day.isSelected(true); currentBottomText.setTypeface(currentBottomText.getTypeface(), Typeface.BOLD); continue; } day.isSelected(false); currentBottomText.setTypeface( TypefaceTextView.getFont(getContext(), TypefaceType .getTypeface(TypefaceType.getDefaultTypeface(getContext())).getAssetFileName()), Typeface.NORMAL); } } @SuppressWarnings("unchecked") @Override public void animatePage(OnSlidePageChangeListener listener, TypedArray attributes) { super.animatePage(listener, attributes); final List<View> children = (List<View>) getTag(R.id.slide_transformer_tag_key); if (children != null) { for (final View child : children) { if (child instanceof ProgressView) { ProgressAttr progressAttr = listener.getDayProgress(mPagePosition, ((ProgressView) child).getIntTag()); ((ProgressView) child).loadStyledAttributes(attributes, progressAttr); animateProgress((ProgressView) child, children, progressAttr); } } } } @SuppressWarnings("unchecked") public void resetStreaks(boolean show) { final List<View> children = (List<View>) getTag(R.id.slide_transformer_tag_key); if (children != null) { for (final View child : children) { if (child instanceof ProgressView) { final ProgressView progressView = (ProgressView) child; progressView.showStreak(show, ProgressView.STREAK.RIGHT_STREAK); progressView.showStreak(show, ProgressView.STREAK.LEFT_STREAK); if (mHasToReanimate) { progressView.showCheckMark(show); } } } } } @Override @SuppressWarnings("unchecked") public void animateStreaks(OnSlidePageChangeListener listener, TypedArray attributes) { final List<View> children = (List<View>) getTag(R.id.slide_transformer_tag_key); if (children != null) { for (final View child : children) { if (child instanceof ProgressView) { final ProgressView progressView = (ProgressView) child; ProgressAttr progressAttr = listener.getDayProgress(mPagePosition, ((ProgressView) child).getIntTag()); ((ProgressView) child).loadStyledAttributes(attributes, progressAttr); progressView.animateStreaks(); } } } } @SuppressWarnings("unchecked") public void resetPage(TypedArray mAttributes) { this.setVisibility(View.VISIBLE); this.setAlpha(1f); loadStyledAttributes(mAttributes); resetStreaks(false); final List<View> children = (List<View>) getTag(R.id.slide_transformer_tag_key); if (children != null) { for (final View child : children) { if (child instanceof ProgressView) { ProgressView progressView = (ProgressView) child; progressView.reset(); } } } } private void animateProgress(ProgressView view, List<View> children, ProgressAttr chartProgressAttr) { if (chartProgressAttr != null) { view.animateProgress(0, chartProgressAttr, mProgressAnimationTime, children); } } /** * Sets the listener for click events in this view * * @param listener */ public void setListener(OnSlideListener listener) { this.mCallback = listener; } }