/* * Copyright (c) 2015 Zhang Hai <Dreaming.in.Code.ZH@Gmail.com> * All Rights Reserved. */ package me.zhanghai.android.douya.util; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.app.Activity; import android.content.Context; import android.content.res.ColorStateList; import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.os.Build; import android.support.design.widget.TextInputLayout; import android.support.v4.view.animation.FastOutLinearInInterpolator; import android.support.v4.view.animation.FastOutSlowInInterpolator; import android.support.v4.widget.DrawerLayout; import android.support.v7.content.res.AppCompatResources; import android.text.Editable; import android.text.TextWatcher; import android.util.DisplayMetrics; import android.util.TypedValue; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.ViewTreeObserver; import android.widget.EditText; import android.widget.TextView; import me.zhanghai.android.douya.ui.ClickableMovementMethod; public class ViewUtils { private ViewUtils() {} public static void fadeOut(final View view, int duration, final boolean gone, final Runnable nextRunnable) { if (view.getVisibility() != View.VISIBLE || view.getAlpha() == 0) { // Cancel any starting animation. view.animate() .alpha(0) .setDuration(0) .start(); view.setVisibility(gone ? View.GONE : View.INVISIBLE); if (nextRunnable != null) { nextRunnable.run(); } return; } view.animate() .alpha(0) .setDuration(duration) .setInterpolator(new FastOutLinearInInterpolator()) .setListener(new AnimatorListenerAdapter() { private boolean mCanceled = false; @Override public void onAnimationCancel(Animator animator) { mCanceled = true; } @Override public void onAnimationEnd(Animator animator) { if (!mCanceled) { view.setVisibility(gone ? View.GONE : View.INVISIBLE); if (nextRunnable != null) { nextRunnable.run(); } } } }) .start(); } public static void fadeOut(View view, int duration, boolean gone) { fadeOut(view, duration, gone, null); } public static void fadeOut(View view, boolean gone) { fadeOut(view, getShortAnimTime(view), gone); } public static void fadeOut(View view) { fadeOut(view, true); } public static void fadeIn(View view, int duration) { if (view.getVisibility() == View.VISIBLE && view.getAlpha() == 1) { // Cancel any starting animation. view.animate() .alpha(1) .setDuration(0) .start(); return; } view.setAlpha(0); view.setVisibility(View.VISIBLE); view.animate() .alpha(1) .setDuration(duration) .setInterpolator(new FastOutSlowInInterpolator()) // NOTE: We need to remove any previously set listener or Android will reuse it. .setListener(null) .start(); } public static void fadeIn(View view) { fadeIn(view, getShortAnimTime(view)); } public static void fadeToVisibility(View view, boolean visible, boolean gone) { if (visible) { fadeIn(view); } else { fadeOut(view, gone); } } public static void fadeToVisibility(View view, boolean visible) { fadeToVisibility(view, visible, true); } public static void crossfade(View fromView, View toView, int duration, boolean gone) { fadeOut(fromView, duration, gone); fadeIn(toView, duration); } public static void crossfade(View fromView, View toView, boolean gone) { crossfade(fromView, toView, getShortAnimTime(fromView), gone); } public static void crossfade(View fromView, View toView) { crossfade(fromView, toView, false); } public static void fadeOutThenFadeIn(final View fromView, final View toView, final int duration, final boolean gone) { fadeOut(fromView, duration, gone, new Runnable() { @Override public void run() { fadeIn(toView, duration); } }); } public static void fadeOutThenFadeIn(View fromView, View toView, boolean gone) { fadeOutThenFadeIn(fromView, toView, getShortAnimTime(fromView), gone); } public static void fadeOutThenFadeIn(final View fromView, final View toView) { fadeOutThenFadeIn(fromView, toView, false); } public static float dpToPx(float dp, Context context) { DisplayMetrics metrics = context.getResources().getDisplayMetrics(); return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, metrics); } public static int dpToPxOffset(float dp, Context context) { return (int) dpToPx(dp, context); } public static int dpToPxSize(float dp, Context context) { float value = dpToPx(dp, context); int size = (int) (value + 0.5f); if (size != 0) { return size; } else if (value == 0) { return 0; } else if (value > 0) { return 1; } else { return -1; } } public static int getColorFromAttrRes(int attrRes, int defaultValue, Context context) { TypedArray a = context.obtainStyledAttributes(new int[] { attrRes }); try { return a.getColor(0, defaultValue); } finally { a.recycle(); } } public static ColorStateList getColorStateListFromAttrRes(int attrRes, Context context) { // TODO: Switch to TintTypedArray when they added this overload. TypedArray a = context.obtainStyledAttributes(new int[] { attrRes }); try { // 0 is an invalid identifier according to the docs of {@link Resources}. int resId = a.getResourceId(0, 0); if (resId != 0) { return AppCompatResources.getColorStateList(context, resId); } return null; } finally { a.recycle(); } } public static Drawable getDrawableFromAttrRes(int attrRes, Context context) { // TODO: Switch to TintTypedArray when they added this overload. TypedArray a = context.obtainStyledAttributes(new int[] { attrRes }); try { // 0 is an invalid identifier according to the docs of {@link Resources}. int resId = a.getResourceId(0, 0); if (resId != 0) { return AppCompatResources.getDrawable(context, resId); } return null; } finally { a.recycle(); } } public static float getFloatFromAttrRes(int attrRes, float defaultValue, Context context) { TypedArray a = context.obtainStyledAttributes(new int[] { attrRes }); try { return a.getFloat(0, defaultValue); } finally { a.recycle(); } } public static int getResIdFromAttrRes(int attrRes, int defaultValue, Context context) { // TODO: Switch to TintTypedArray when they added this overload. TypedArray a = context.obtainStyledAttributes(new int[] { attrRes }); try { return a.getResourceId(0, defaultValue); } finally { a.recycle(); } } public static int getShortAnimTime(Resources resources) { return resources.getInteger(android.R.integer.config_shortAnimTime); } public static int getShortAnimTime(View view) { return getShortAnimTime(view.getResources()); } public static int getShortAnimTime(Context context) { return getShortAnimTime(context.getResources()); } public static int getMediumAnimTime(Resources resources) { return resources.getInteger(android.R.integer.config_mediumAnimTime); } public static int getMediumAnimTime(View view) { return getMediumAnimTime(view.getResources()); } public static int getMediumAnimTime(Context context) { return getMediumAnimTime(context.getResources()); } public static int getLongAnimTime(Resources resources) { return resources.getInteger(android.R.integer.config_longAnimTime); } public static int getLongAnimTime(View view) { return getLongAnimTime(view.getResources()); } public static int getLongAnimTime(Context context) { return getLongAnimTime(context.getResources()); } public static int getWidthExcludingPadding(View view) { return Math.max(0, view.getWidth() - view.getPaddingLeft() - view.getPaddingRight()); } public static int getHeightExcludingPadding(View view) { return Math.max(0, view.getHeight() - view.getPaddingTop() - view.getPaddingBottom()); } private static boolean hasSwDp(int dp, Context context) { return context.getResources().getConfiguration().smallestScreenWidthDp >= dp; } public static boolean hasSw600Dp(Context context) { return hasSwDp(600, context); } private static boolean hasWDp(int dp, Context context) { return context.getResources().getConfiguration().screenWidthDp >= dp; } public static boolean hasW600Dp(Context context) { return hasWDp(600, context); } public static boolean hasW960Dp(Context context) { return hasWDp(960, context); } public static void hideTextInputLayoutErrorOnTextChange(EditText editText, final TextInputLayout textInputLayout) { editText.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {} @Override public void onTextChanged(CharSequence s, int start, int before, int count) {} @Override public void afterTextChanged(Editable s) { textInputLayout.setError(null); } }); } public static View inflate(int resource, ViewGroup parent) { return LayoutInflater.from(parent.getContext()).inflate(resource, parent, false); } public static View inflateInto(int resource, ViewGroup parent) { return LayoutInflater.from(parent.getContext()).inflate(resource, parent); } public static boolean isInLandscape(Context context) { return context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE; } public static boolean isVisible(View view) { return view.getVisibility() == View.VISIBLE; } public static void postOnDrawerClosed(final DrawerLayout drawerLayout, final Runnable runnable) { drawerLayout.addDrawerListener(new DrawerLayout.SimpleDrawerListener() { @Override public void onDrawerClosed(View drawerView) { drawerLayout.removeDrawerListener(this); runnable.run(); } }); } public static void postOnPreDraw(final View view, final Runnable runnable) { view.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { @Override public boolean onPreDraw() { view.getViewTreeObserver().removeOnPreDrawListener(this); runnable.run(); return true; } }); } public static float pxToDp(float px, Context context) { DisplayMetrics metrics = context.getResources().getDisplayMetrics(); return px / metrics.density; } public static int pxToDpInt(float px, Context context) { return Math.round(pxToDp(px, context)); } public static void replaceChild(ViewGroup viewGroup, View oldChild, View newChild) { int index = viewGroup.indexOfChild(oldChild); viewGroup.removeViewAt(index); viewGroup.addView(newChild, index); } public static void setHeight(View view, int height) { ViewGroup.LayoutParams layoutParams = view.getLayoutParams(); if (layoutParams.height == height) { return; } layoutParams.height = height; view.setLayoutParams(layoutParams); } public static void setSize(View view, int size) { ViewGroup.LayoutParams layoutParams = view.getLayoutParams(); if (layoutParams.width == size && layoutParams.height == size) { return; } layoutParams.width = size; layoutParams.height = size; view.setLayoutParams(layoutParams); } public static void setLayoutFullscreen(View view) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { view.setSystemUiVisibility( View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN); } } public static void setLayoutFullscreen(Activity activity) { setLayoutFullscreen(activity.getWindow().getDecorView()); } public static void setTextViewBold(TextView textView, boolean bold) { Typeface typeface = textView.getTypeface(); if (typeface.isBold() == bold) { return; } int style = textView.getTypeface().getStyle(); if (bold) { style |= Typeface.BOLD; } else { style &= ~Typeface.BOLD; } // Workaround insane behavior in TextView#setTypeface(Typeface, int). if (style > 0) { textView.setTypeface(typeface, style); } else { textView.setTypeface(Typeface.create(typeface, style), style); } } public static void setTextViewItalic(TextView textView, boolean italic) { Typeface typeface = textView.getTypeface(); if (typeface.isItalic() == italic) { return; } int style = textView.getTypeface().getStyle(); if (italic) { style |= Typeface.ITALIC; } else { style &= ~Typeface.ITALIC; } // Workaround insane behavior in TextView#setTypeface(Typeface, int). if (style > 0) { textView.setTypeface(typeface, style); } else { textView.setTypeface(Typeface.create(typeface, style), style); } } public static void setTextViewLinkClickable(TextView textView) { textView.setMovementMethod(ClickableMovementMethod.getInstance()); // Reset for TextView.fixFocusableAndClickableSettings(). We don't want View.onTouchEvent() // to consume touch events. textView.setClickable(false); textView.setLongClickable(false); } public static void setVisibleOrGone(View view, boolean visible) { view.setVisibility(visible ? View.VISIBLE : View.GONE); } public static void setVisibleOrInvisible(View view, boolean visible) { view.setVisibility(visible ? View.VISIBLE : View.INVISIBLE); } public static void setWidth(View view, int width) { ViewGroup.LayoutParams layoutParams = view.getLayoutParams(); if (layoutParams.width == width) { return; } layoutParams.width = width; view.setLayoutParams(layoutParams); } }