ref = mListeners.get(i);
+ if(ref.get() == null)
+ mListeners.remove(i);
+ else
+ ref.get().onThemeChanged(event);
+ }
+ }
+ }
+
+ public interface OnThemeChangedListener{
+
+ void onThemeChanged(@Nullable OnThemeChangedEvent event);
+
+ }
+
+ public static class OnThemeChangedEvent{
+ public final int theme;
+
+ public OnThemeChangedEvent(int theme){
+ this.theme = theme;
+ }
+ }
+
+
+}
diff --git a/lib/src/main/java/com/rey/material/app/TimePickerDialog.java b/lib/src/main/java/com/rey/material/app/TimePickerDialog.java
index 573839c0..c274b6a1 100644
--- a/lib/src/main/java/com/rey/material/app/TimePickerDialog.java
+++ b/lib/src/main/java/com/rey/material/app/TimePickerDialog.java
@@ -143,9 +143,9 @@ public String getFormattedTime(DateFormat formatter){
private class TimePickerLayout extends android.widget.FrameLayout implements View.OnClickListener, TimePicker.OnTimeChangedListener{
private int mHeaderHeight;
- private int mTextTimeColor;
+ private int mTextTimeColor = 0xFF000000;
private int mTextTimeSize;
- private boolean mIsLeadingZero;
+ private boolean mIsLeadingZero = false;
private boolean mIsAm = true;
private int mCheckBoxSize;
@@ -212,21 +212,38 @@ public TimePickerLayout(Context context) {
addView(mPmView);
setWillNotDraw(false);
+
+ mCheckBoxSize = ThemeUtil.dpToPx(context, 48);
+ mHeaderHeight = ThemeUtil.dpToPx(context, 120);
+ mTextTimeSize = context.getResources().getDimensionPixelOffset(R.dimen.abc_text_size_headline_material);
}
public void applyStyle(int resId){
mTimePicker.applyStyle(resId);
Context context = getContext();
- mCheckBoxSize = ThemeUtil.dpToPx(context, 48);
-
TypedArray a = context.obtainStyledAttributes(resId, R.styleable.TimePickerDialog);
- mHeaderHeight = a.getDimensionPixelSize(R.styleable.TimePickerDialog_tp_headerHeight, ThemeUtil.dpToPx(context, 120));
- mTextTimeColor = a.getColor(R.styleable.TimePickerDialog_tp_textTimeColor, 0xFF000000);
- mTextTimeSize = a.getDimensionPixelSize(R.styleable.TimePickerDialog_tp_textTimeSize, context.getResources().getDimensionPixelOffset(R.dimen.abc_text_size_headline_material));
- mIsLeadingZero = a.getBoolean(R.styleable.TimePickerDialog_tp_leadingZero, false);
- String am = a.getString(R.styleable.TimePickerDialog_tp_am);
- String pm = a.getString(R.styleable.TimePickerDialog_tp_pm);
+
+ String am = null;
+ String pm = null;
+
+ for(int i = 0, count = a.getIndexCount(); i < count; i++){
+ int attr = a.getIndex(i);
+
+ if(attr == R.styleable.TimePickerDialog_tp_headerHeight)
+ mHeaderHeight = a.getDimensionPixelSize(attr, 0);
+ else if(attr == R.styleable.TimePickerDialog_tp_textTimeColor)
+ mTextTimeColor = a.getColor(attr, 0);
+ else if(attr == R.styleable.TimePickerDialog_tp_textTimeSize)
+ mTextTimeSize = a.getDimensionPixelSize(attr, 0);
+ else if(attr == R.styleable.TimePickerDialog_tp_leadingZero)
+ mIsLeadingZero = a.getBoolean(attr, false);
+ else if(attr == R.styleable.TimePickerDialog_tp_am)
+ am = a.getString(attr);
+ else if(attr == R.styleable.TimePickerDialog_tp_pm)
+ pm = a.getString(attr);
+ }
+
a.recycle();
if(am == null)
@@ -243,6 +260,7 @@ public void applyStyle(int resId){
mTimePicker.getTextColor(),
mTimePicker.getTextHighlightColor(),
};
+
mAmView.setBackgroundColor(mTimePicker.getSelectionColor());
mAmView.setAnimDuration(mTimePicker.getAnimDuration());
mAmView.setInterpolator(mTimePicker.getInInterpolator(), mTimePicker.getOutInterpolator());
diff --git a/lib/src/main/java/com/rey/material/app/ToolbarManager.java b/lib/src/main/java/com/rey/material/app/ToolbarManager.java
index b6a7667a..5f41ff66 100644
--- a/lib/src/main/java/com/rey/material/app/ToolbarManager.java
+++ b/lib/src/main/java/com/rey/material/app/ToolbarManager.java
@@ -1,10 +1,13 @@
package com.rey.material.app;
import android.os.Build;
+import android.support.annotation.Nullable;
import android.support.v4.app.FragmentManager;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarActivity;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.app.AppCompatDelegate;
import android.support.v7.widget.ActionMenuView;
import android.support.v7.widget.Toolbar;
import android.view.Gravity;
@@ -30,7 +33,7 @@
*/
public class ToolbarManager {
- private ActionBarActivity mActivity;
+ private AppCompatDelegate mAppCompatDelegate;
private Toolbar mToolbar;
private int mRippleStyle;
private Animator mAnimator;
@@ -73,8 +76,8 @@ public void onAnimationStart(Animation animation) {
@Override
public void onAnimationEnd(Animation animation) {
- if(mActivity != null)
- mActivity.supportInvalidateOptionsMenu();
+ if(mAppCompatDelegate != null)
+ mAppCompatDelegate.invalidateOptionsMenu();
else
onPrepareMenu();
}
@@ -86,17 +89,17 @@ public void onAnimationRepeat(Animation animation) {
private NavigationManager mNavigationManager;
- public ToolbarManager(ActionBarActivity activity, Toolbar toolbar, int defaultGroupId, int rippleStyle, int animIn, int animOut){
- this(activity, toolbar, defaultGroupId, rippleStyle, new SimpleAnimator(animIn, animOut));
+ public ToolbarManager(AppCompatDelegate delegate, Toolbar toolbar, int defaultGroupId, int rippleStyle, int animIn, int animOut){
+ this(delegate, toolbar, defaultGroupId, rippleStyle, new SimpleAnimator(animIn, animOut));
}
- public ToolbarManager(ActionBarActivity activity, Toolbar toolbar, int defaultGroupId, int rippleStyle, Animator animator){
- mActivity = activity;
+ public ToolbarManager(AppCompatDelegate delegate, Toolbar toolbar, int defaultGroupId, int rippleStyle, Animator animator){
+ mAppCompatDelegate = delegate;
mToolbar = toolbar;
mCurrentGroup = defaultGroupId;
mRippleStyle = rippleStyle;
mAnimator = animator;
- mActivity.setSupportActionBar(toolbar);
+ mAppCompatDelegate.setSupportActionBar(toolbar);
}
/**
@@ -164,7 +167,7 @@ public void setCurrentGroup(int groupId){
public void createMenu(int menuId){
mToolbar.inflateMenu(menuId);
mMenuDataChanged = true;
- if(mActivity == null)
+ if(mAppCompatDelegate == null)
onPrepareMenu();
}
@@ -179,7 +182,7 @@ public void onPrepareMenu(){
Menu menu = mToolbar.getMenu();
for(int i = 0, count = menu.size(); i < count; i++){
MenuItem item = menu.getItem(i);
- item.setVisible(item.getGroupId() == mCurrentGroup);
+ item.setVisible(item.getGroupId() == mCurrentGroup || item.getGroupId() == 0);
}
mMenuDataChanged = false;
@@ -191,6 +194,7 @@ public void onPrepareMenu(){
*/
public void setNavigationManager(NavigationManager navigationManager){
mNavigationManager = navigationManager;
+ notifyNavigationStateInvalidated();
}
/**
@@ -363,9 +367,9 @@ public static abstract class NavigationManager{
/**
* @param styleId the style res of navigation icon.
*/
- public NavigationManager(int styleId, Toolbar toolbar){
+ public NavigationManager(NavigationDrawerDrawable navigationIcon, Toolbar toolbar){
mToolbar = toolbar;
- mNavigationIcon = new NavigationDrawerDrawable.Builder(mToolbar.getContext(), styleId).build();
+ mNavigationIcon = navigationIcon;
mToolbar.setNavigationIcon(mNavigationIcon);
mToolbar.setNavigationOnClickListener(new View.OnClickListener(){
@Override
@@ -422,13 +426,13 @@ public static class BaseNavigationManager extends NavigationManager{
/**
*
- * @param styledId the style res of navigation icon.
+ * @param styleId the resourceId of navigation icon style.
* @param drawerLayout can be null if you don't need to handle navigation state when open/close navigation drawer.
*/
- public BaseNavigationManager(int styledId, ActionBarActivity activity, Toolbar toolbar, DrawerLayout drawerLayout){
- super(styledId, toolbar);
+ public BaseNavigationManager(int styleId, FragmentManager fragmentManager, Toolbar toolbar, DrawerLayout drawerLayout){
+ super(new NavigationDrawerDrawable.Builder(toolbar.getContext(), styleId).build(), toolbar);
mDrawerLayout = drawerLayout;
- mFragmentManager = activity.getSupportFragmentManager();
+ mFragmentManager = fragmentManager;
if(mDrawerLayout != null)
mDrawerLayout.setDrawerListener(new DrawerLayout.DrawerListener() {
@@ -512,4 +516,38 @@ protected void onDrawerStateChanged(int newState) {
}
}
+
+ /**
+ * A Manager class extend from {@link BaseNavigationManager} class and add theme supporting.
+ */
+ public static class ThemableNavigationManager extends BaseNavigationManager implements ThemeManager.OnThemeChangedListener{
+
+ private int mStyleId;
+ private int mCurrentStyle;
+
+ /**
+ *
+ * @param styleId the styleId of navigation icon.
+ * @param drawerLayout can be null if you don't need to handle navigation state when open/close navigation drawer.
+ */
+ public ThemableNavigationManager(int styleId, FragmentManager fragmentManager, Toolbar toolbar, DrawerLayout drawerLayout){
+ super(ThemeManager.getInstance().getCurrentStyle(styleId), fragmentManager, toolbar, drawerLayout);
+ mStyleId = styleId;
+ mCurrentStyle = ThemeManager.getInstance().getCurrentStyle(styleId);
+ ThemeManager.getInstance().registerOnThemeChangedListener(this);
+ }
+
+ @Override
+ public void onThemeChanged(@Nullable ThemeManager.OnThemeChangedEvent event) {
+ int style = ThemeManager.getInstance().getCurrentStyle(mStyleId);
+ if(mCurrentStyle != style){
+ mCurrentStyle = style;
+ NavigationDrawerDrawable drawable = new NavigationDrawerDrawable.Builder(mToolbar.getContext(), mCurrentStyle).build();
+ drawable.switchIconState(mNavigationIcon.getIconState(), false);
+ mNavigationIcon = drawable;
+ mToolbar.setNavigationIcon(mNavigationIcon);
+ }
+ }
+
+ }
}
diff --git a/lib/src/main/java/com/rey/material/drawable/ArrowDrawable.java b/lib/src/main/java/com/rey/material/drawable/ArrowDrawable.java
index 42e5eb20..a934fc70 100644
--- a/lib/src/main/java/com/rey/material/drawable/ArrowDrawable.java
+++ b/lib/src/main/java/com/rey/material/drawable/ArrowDrawable.java
@@ -58,7 +58,26 @@ public void setColor(ColorStateList colorStateList){
mColorStateList = colorStateList;
onStateChange(getState());
}
-
+
+ public void setAnimationDuration(int duration){
+ mAnimDuration = duration;
+ }
+
+ public void setInterpolator(Interpolator interpolator){
+ mInterpolator = interpolator;
+ }
+
+ public void setClockwise(boolean clockwise){
+ mClockwise = clockwise;
+ }
+
+ public void setArrowSize(int size){
+ if(mSize != size){
+ mSize = size;
+ invalidateSelf();
+ }
+ }
+
public void setMode(int mode, boolean animation){
if(mMode != mode){
mMode = mode;
diff --git a/lib/src/main/java/com/rey/material/drawable/CircularProgressDrawable.java b/lib/src/main/java/com/rey/material/drawable/CircularProgressDrawable.java
index 9aafbd11..aeabd311 100644
--- a/lib/src/main/java/com/rey/material/drawable/CircularProgressDrawable.java
+++ b/lib/src/main/java/com/rey/material/drawable/CircularProgressDrawable.java
@@ -98,7 +98,84 @@ private CircularProgressDrawable(int padding, float initialAngle, float progress
mRect = new RectF();
}
-
+
+ public void applyStyle(Context context, int resId){
+ TypedArray a = context.obtainStyledAttributes(resId, R.styleable.CircularProgressDrawable);
+
+ int strokeColor = 0;
+ boolean strokeColorDefined = false;
+ int[] strokeColors = null;
+
+ for(int i = 0, count = a.getIndexCount(); i < count; i++){
+ int attr = a.getIndex(i);
+
+ if(attr == R.styleable.CircularProgressDrawable_cpd_padding)
+ mPadding = a.getDimensionPixelSize(attr, 0);
+ else if(attr == R.styleable.CircularProgressDrawable_cpd_initialAngle)
+ mInitialAngle = a.getInteger(attr, 0);
+ else if(attr == R.styleable.CircularProgressDrawable_pv_progress)
+ setProgress(a.getFloat(attr, 0));
+ else if(attr == R.styleable.CircularProgressDrawable_pv_secondaryProgress)
+ setSecondaryProgress(a.getFloat(attr, 0));
+ else if(attr == R.styleable.CircularProgressDrawable_cpd_maxSweepAngle)
+ mMaxSweepAngle = a.getInteger(attr, 0);
+ else if(attr == R.styleable.CircularProgressDrawable_cpd_minSweepAngle)
+ mMinSweepAngle = a.getInteger(attr, 0);
+ else if(attr == R.styleable.CircularProgressDrawable_cpd_strokeSize)
+ mStrokeSize = a.getDimensionPixelSize(attr, 0);
+ else if(attr == R.styleable.CircularProgressDrawable_cpd_strokeColor) {
+ strokeColor = a.getColor(attr, 0);
+ strokeColorDefined = true;
+ }
+ else if(attr == R.styleable.CircularProgressDrawable_cpd_strokeColors){
+ TypedArray ta = context.getResources().obtainTypedArray(a.getResourceId(attr, 0));
+ strokeColors = new int[ta.length()];
+ for(int j = 0; j < ta.length(); j++)
+ strokeColors[j] = ta.getColor(j, 0);
+ ta.recycle();
+ }
+ else if(attr == R.styleable.CircularProgressDrawable_cpd_strokeSecondaryColor)
+ mStrokeSecondaryColor = a.getColor(attr, 0);
+ else if(attr == R.styleable.CircularProgressDrawable_cpd_reverse)
+ mReverse = a.getBoolean(attr, false);
+ else if(attr == R.styleable.CircularProgressDrawable_cpd_rotateDuration)
+ mRotateDuration = a.getInteger(attr, 0);
+ else if(attr == R.styleable.CircularProgressDrawable_cpd_transformDuration)
+ mTransformDuration = a.getInteger(attr, 0);
+ else if(attr == R.styleable.CircularProgressDrawable_cpd_keepDuration)
+ mKeepDuration = a.getInteger(attr, 0);
+ else if(attr == R.styleable.CircularProgressDrawable_cpd_transformInterpolator)
+ mTransformInterpolator = AnimationUtils.loadInterpolator(context, a.getResourceId(attr, 0));
+ else if(attr == R.styleable.CircularProgressDrawable_pv_progressMode)
+ mProgressMode = a.getInteger(attr, 0);
+ else if(attr == R.styleable.CircularProgressDrawable_cpd_inAnimDuration)
+ mInAnimationDuration = a.getInteger(attr, 0);
+ else if(attr == R.styleable.CircularProgressDrawable_cpd_inStepColors){
+ TypedArray ta = context.getResources().obtainTypedArray(a.getResourceId(attr, 0));
+ mInColors = new int[ta.length()];
+ for(int j = 0; j < ta.length(); j++)
+ mInColors[j] = ta.getColor(j, 0);
+ ta.recycle();
+ }
+ else if(attr == R.styleable.CircularProgressDrawable_cpd_inStepPercent)
+ mInStepPercent = a.getFloat(attr, 0);
+ else if(attr == R.styleable.CircularProgressDrawable_cpd_outAnimDuration)
+ mOutAnimationDuration = a.getInteger(attr, 0);
+ }
+
+ a.recycle();
+
+ if(strokeColors != null)
+ mStrokeColors = strokeColors;
+ else if(strokeColorDefined)
+ mStrokeColors = new int[]{strokeColor};
+
+ if(mStrokeColorIndex >= mStrokeColors.length)
+ mStrokeColorIndex = 0;
+
+ invalidateSelf();
+ }
+
@Override
public void draw(Canvas canvas) {
switch (mProgressMode) {
diff --git a/lib/src/main/java/com/rey/material/drawable/DividerDrawable.java b/lib/src/main/java/com/rey/material/drawable/DividerDrawable.java
index 2b5d496d..1a359fdb 100644
--- a/lib/src/main/java/com/rey/material/drawable/DividerDrawable.java
+++ b/lib/src/main/java/com/rey/material/drawable/DividerDrawable.java
@@ -62,6 +62,18 @@ public DividerDrawable(int height, int paddingLeft, int paddingRight, ColorState
mAnimEnable = true;
}
+ public void setDividerHeight(int height){
+ if(mHeight != height){
+ mHeight = height;
+ mPaint.setStrokeWidth(mHeight);
+ invalidateSelf();
+ }
+ }
+
+ public int getDividerHeight(){
+ return mHeight;
+ }
+
public void setPadding(int left, int right){
if(mPaddingLeft != left || mPaddingRight != right){
mPaddingLeft = left;
@@ -90,7 +102,11 @@ public void setColor(ColorStateList colorStateList){
mColorStateList = colorStateList;
onStateChange(getState());
}
-
+
+ public void setAnimationDuration(int duration){
+ mAnimDuration = duration;
+ }
+
private PathEffect getPathEffect(){
if(mPathEffect == null)
mPathEffect = new DashPathEffect(new float[]{0.2f, mHeight * 2}, 0f);
@@ -105,7 +121,7 @@ public void draw(Canvas canvas) {
Rect bounds = getBounds();
float y = bounds.bottom - mHeight / 2;
-
+
if(!isRunning()){
mPath.reset();
mPath.moveTo(bounds.left + mPaddingLeft, y);
@@ -165,7 +181,7 @@ protected boolean onStateChange(int[] state) {
int color = mColorStateList.getColorForState(state, mCurColor);
if(mCurColor != color){
- if(!mInEditMode && mAnimEnable && mEnable){
+ if(!mInEditMode && mAnimEnable && mEnable && mAnimDuration > 0){
mPrevColor = isRunning() ? mPrevColor : mCurColor;
mCurColor = color;
start();
diff --git a/lib/src/main/java/com/rey/material/drawable/LinearProgressDrawable.java b/lib/src/main/java/com/rey/material/drawable/LinearProgressDrawable.java
index 35793fc0..45d2cc32 100644
--- a/lib/src/main/java/com/rey/material/drawable/LinearProgressDrawable.java
+++ b/lib/src/main/java/com/rey/material/drawable/LinearProgressDrawable.java
@@ -105,6 +105,90 @@ private LinearProgressDrawable(float progressPercent, float secondaryProgressPer
mPath = new Path();
}
+
+ public void applyStyle(Context context, int resId){
+ TypedArray a = context.obtainStyledAttributes(resId, R.styleable.LinearProgressDrawable);
+
+ int strokeColor = 0;
+ boolean strokeColorDefined = false;
+ int[] strokeColors = null;
+
+ for(int i = 0, count = a.getIndexCount(); i < count; i++){
+ int attr = a.getIndex(i);
+
+ if(attr == R.styleable.LinearProgressDrawable_pv_progress)
+ setProgress(a.getFloat(attr, 0));
+ else if(attr == R.styleable.LinearProgressDrawable_pv_secondaryProgress)
+ setSecondaryProgress(a.getFloat(attr, 0));
+ else if(attr == R.styleable.LinearProgressDrawable_lpd_maxLineWidth){
+ TypedValue value = a.peekValue(attr);
+ if(value.type == TypedValue.TYPE_FRACTION) {
+ mMaxLineWidthPercent = a.getFraction(attr, 1, 1, 0.75f);
+ mMaxLineWidth = 0;
+ }
+ else {
+ mMaxLineWidth = a.getDimensionPixelSize(attr, 0);
+ mMaxLineWidthPercent = 0f;
+ }
+ }
+ else if(attr == R.styleable.LinearProgressDrawable_lpd_minLineWidth){
+ TypedValue value = a.peekValue(attr);
+ if(value.type == TypedValue.TYPE_FRACTION) {
+ mMinLineWidthPercent = a.getFraction(attr, 1, 1, 0.25f);
+ mMinLineWidth = 0;
+ }
+ else {
+ mMinLineWidth = a.getDimensionPixelSize(attr, 0);
+ mMinLineWidthPercent = 0f;
+ }
+ }
+ else if(attr == R.styleable.LinearProgressDrawable_lpd_strokeSize)
+ mStrokeSize = a.getDimensionPixelSize(attr, 0);
+ else if(attr == R.styleable.LinearProgressDrawable_lpd_verticalAlign)
+ mVerticalAlign = a.getInteger(attr, 0);
+ else if(attr == R.styleable.LinearProgressDrawable_lpd_strokeColor) {
+ strokeColor = a.getColor(attr, 0);
+ strokeColorDefined = true;
+ }
+ else if(attr == R.styleable.LinearProgressDrawable_lpd_strokeColors){
+ TypedArray ta = context.getResources().obtainTypedArray(a.getResourceId(attr, 0));
+ strokeColors = new int[ta.length()];
+ for(int j = 0; j < ta.length(); j++)
+ strokeColors[j] = ta.getColor(j, 0);
+ ta.recycle();
+ }
+ else if(attr == R.styleable.LinearProgressDrawable_lpd_strokeSecondaryColor)
+ mStrokeSecondaryColor = a.getColor(attr, 0);
+ else if(attr == R.styleable.LinearProgressDrawable_lpd_reverse)
+ mReverse = a.getBoolean(attr, false);
+ else if(attr == R.styleable.LinearProgressDrawable_lpd_travelDuration)
+ mTravelDuration = a.getInteger(attr, 0);
+ else if(attr == R.styleable.LinearProgressDrawable_lpd_transformDuration)
+ mTransformDuration = a.getInteger(attr, 0);
+ else if(attr == R.styleable.LinearProgressDrawable_lpd_keepDuration)
+ mKeepDuration = a.getInteger(attr, 0);
+ else if(attr == R.styleable.LinearProgressDrawable_lpd_transformInterpolator)
+ mTransformInterpolator = AnimationUtils.loadInterpolator(context, a.getResourceId(attr, 0));
+ else if(attr == R.styleable.LinearProgressDrawable_pv_progressMode)
+ mProgressMode = a.getInteger(attr, 0);
+ else if(attr == R.styleable.LinearProgressDrawable_lpd_inAnimDuration)
+ mInAnimationDuration = a.getInteger(attr, 0);
+ else if(attr == R.styleable.LinearProgressDrawable_lpd_outAnimDuration)
+ mOutAnimationDuration = a.getInteger(attr, 0);
+ }
+
+ a.recycle();
+
+ if(strokeColors != null)
+ mStrokeColors = strokeColors;
+ else if(strokeColorDefined)
+ mStrokeColors = new int[]{strokeColor};
+
+ if(mStrokeColorIndex >= mStrokeColors.length)
+ mStrokeColorIndex = 0;
+
+ invalidateSelf();
+ }
@Override
public void draw(Canvas canvas) {
diff --git a/lib/src/main/java/com/rey/material/drawable/NavigationDrawerDrawable.java b/lib/src/main/java/com/rey/material/drawable/NavigationDrawerDrawable.java
index 87eb45e8..e9cd849a 100644
--- a/lib/src/main/java/com/rey/material/drawable/NavigationDrawerDrawable.java
+++ b/lib/src/main/java/com/rey/material/drawable/NavigationDrawerDrawable.java
@@ -37,6 +37,10 @@ public int getIconState(){
public boolean setIconState(int state, float progress){
return mLineDrawable.setLineState(state, progress);
}
+
+ public float getIconAnimProgress(){
+ return mLineDrawable.getAnimProgress();
+ }
@Override
public void draw(Canvas canvas) {
@@ -120,9 +124,6 @@ public Builder(Context context, AttributeSet attrs, int defStyleAttr, int defSty
if(lineId > 0){
LineMorphingDrawable.Builder builder = new LineMorphingDrawable.Builder(context, lineId);
-
- builder.states(new LineMorphingDrawable.State(new float[]{0f, 0.1f, 1f, 0.1f, 0f, 0.5f, 1f, 0.5f, 0f, 0.9f, 1f, 0.9f}, null), new LineMorphingDrawable.State(new float[]{0.5f, 0f, 1f, 0.5f, 0f, 0.5f, 1f, 0.5f, 0.5f, 1f, 1f, 0.5f}, new int[]{0, 2}));
-
line(builder.build());
}
diff --git a/lib/src/main/java/com/rey/material/drawable/OvalShadowDrawable.java b/lib/src/main/java/com/rey/material/drawable/OvalShadowDrawable.java
new file mode 100644
index 00000000..892ee070
--- /dev/null
+++ b/lib/src/main/java/com/rey/material/drawable/OvalShadowDrawable.java
@@ -0,0 +1,360 @@
+package com.rey.material.drawable;
+
+import android.content.res.ColorStateList;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.PixelFormat;
+import android.graphics.RadialGradient;
+import android.graphics.RectF;
+import android.graphics.Shader;
+import android.graphics.drawable.Animatable;
+import android.graphics.drawable.Drawable;
+import android.os.SystemClock;
+
+import com.rey.material.util.ColorUtil;
+import com.rey.material.util.ViewUtil;
+
+/**
+ * Created by Rey on 5/19/2015.
+ */
+public class OvalShadowDrawable extends Drawable implements Animatable {
+
+ private boolean mRunning = false;
+ private long mStartTime;
+ private float mAnimProgress;
+ private int mAnimDuration;
+
+ private boolean mEnable = true;
+ private boolean mInEditMode = false;
+ private boolean mAnimEnable = true;
+
+ private Paint mShadowPaint;
+ private Paint mGlowPaint;
+ private Paint mPaint;
+
+ private int mRadius;
+ private float mShadowSize;
+ private float mShadowOffset;
+
+ private Path mShadowPath;
+ private Path mGlowPath;
+
+ private RectF mTempRect = new RectF();
+
+ private ColorStateList mColorStateList;
+ private int mPrevColor;
+ private int mCurColor;
+
+ private boolean mNeedBuildShadow = true;
+
+ private static final int COLOR_SHADOW_START = 0x4C000000;
+ private static final int COLOR_SHADOW_END = 0x00000000;
+
+ public OvalShadowDrawable(int radius, ColorStateList colorStateList, float shadowSize, float shadowOffset, int animDuration){
+ mAnimDuration = animDuration;
+
+ mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
+ mPaint.setStyle(Paint.Style.FILL);
+
+ setColor(colorStateList);
+ setRadius(radius);
+ setShadow(shadowSize, shadowOffset);
+ }
+
+ public boolean setRadius(int radius){
+ if(mRadius != radius){
+ mRadius = radius;
+ mNeedBuildShadow = true;
+ invalidateSelf();
+
+ return true;
+ }
+
+ return false;
+ }
+
+ public boolean setShadow(float size, float offset){
+ if(mShadowSize != size || mShadowOffset != offset){
+ mShadowSize = size;
+ mShadowOffset = offset;
+ mNeedBuildShadow = true;
+ invalidateSelf();
+
+ return true;
+ }
+
+ return false;
+ }
+
+ public boolean setAnimationDuration(int duration){
+ if(mAnimDuration != duration){
+ mAnimDuration = duration;
+ return true;
+ }
+
+ return false;
+ }
+
+ public void setColor(ColorStateList colorStateList){
+ mColorStateList = colorStateList;
+ onStateChange(getState());
+ }
+
+ public void setColor(int color){
+ mColorStateList = ColorStateList.valueOf(color);
+ onStateChange(getState());
+ }
+
+ public ColorStateList getColor(){
+ return mColorStateList;
+ }
+
+ public void setInEditMode(boolean b){
+ mInEditMode = b;
+ }
+
+ public void setAnimEnable(boolean b){
+ mAnimEnable = b;
+ }
+
+ public int getRadius(){
+ return mRadius;
+ }
+
+ public float getShadowSize(){
+ return mShadowSize;
+ }
+
+ public float getShadowOffset(){
+ return mShadowOffset;
+ }
+
+ public float getPaddingLeft(){
+ return mShadowSize;
+ }
+
+ public float getPaddingTop(){
+ return mShadowSize;
+ }
+
+ public float getPaddingRight(){
+ return mShadowSize;
+ }
+
+ public float getPaddingBottom(){
+ return mShadowSize + mShadowOffset;
+ }
+
+ public float getCenterX(){
+ return mRadius + mShadowSize;
+ }
+
+ public float getCenterY(){
+ return mRadius + mShadowSize;
+ }
+
+ public boolean isPointerOver(float x, float y){
+ float distance = (float)Math.sqrt(Math.pow(x - getCenterX(), 2) + Math.pow(y - getCenterY(), 2));
+
+ return distance < mRadius;
+ }
+
+ @Override
+ public int getIntrinsicWidth() {
+ return (int)((mRadius + mShadowSize) * 2 + 0.5f);
+ }
+
+ @Override
+ public int getIntrinsicHeight() {
+ return (int)((mRadius + mShadowSize) * 2 + mShadowOffset + 0.5f);
+ }
+
+ private void buildShadow(){
+ if(mShadowSize <= 0)
+ return;
+
+ if(mShadowPaint == null){
+ mShadowPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
+ mShadowPaint.setStyle(Paint.Style.FILL);
+ mShadowPaint.setDither(true);
+ }
+ float startRatio = (float)mRadius / (mRadius + mShadowSize + mShadowOffset);
+ mShadowPaint.setShader(new RadialGradient(0, 0, mRadius + mShadowSize,
+ new int[]{COLOR_SHADOW_START, COLOR_SHADOW_START, COLOR_SHADOW_END},
+ new float[]{0f, startRatio, 1f}
+ , Shader.TileMode.CLAMP));
+
+ if(mShadowPath == null){
+ mShadowPath = new Path();
+ mShadowPath.setFillType(Path.FillType.EVEN_ODD);
+ }
+ else
+ mShadowPath.reset();
+ float radius = mRadius + mShadowSize;
+ mTempRect.set(-radius, -radius, radius, radius);
+ mShadowPath.addOval(mTempRect, Path.Direction.CW);
+ radius = mRadius - 1;
+ mTempRect.set(-radius, -radius - mShadowOffset, radius, radius - mShadowOffset);
+ mShadowPath.addOval(mTempRect, Path.Direction.CW);
+
+ if(mGlowPaint == null){
+ mGlowPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
+ mGlowPaint.setStyle(Paint.Style.FILL);
+ mGlowPaint.setDither(true);
+ }
+ startRatio = (mRadius - mShadowSize / 2f) / (mRadius + mShadowSize / 2f);
+ mGlowPaint.setShader(new RadialGradient(0, 0, mRadius + mShadowSize / 2f,
+ new int[]{COLOR_SHADOW_START, COLOR_SHADOW_START, COLOR_SHADOW_END},
+ new float[]{0f, startRatio, 1f}
+ , Shader.TileMode.CLAMP));
+
+ if(mGlowPath == null){
+ mGlowPath = new Path();
+ mGlowPath.setFillType(Path.FillType.EVEN_ODD);
+ }
+ else
+ mGlowPath.reset();
+
+ radius = mRadius + mShadowSize / 2f;
+ mTempRect.set(-radius, -radius, radius, radius);
+ mGlowPath.addOval(mTempRect, Path.Direction.CW);
+ radius = mRadius - 1;
+ mTempRect.set(-radius, -radius, radius, radius);
+ mGlowPath.addOval(mTempRect, Path.Direction.CW);
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ if(mNeedBuildShadow){
+ buildShadow();
+ mNeedBuildShadow = false;
+ }
+ int saveCount;
+
+ if(mShadowSize > 0){
+ saveCount = canvas.save();
+ canvas.translate(mShadowSize + mRadius, mShadowSize + mRadius + mShadowOffset);
+ canvas.drawPath(mShadowPath, mShadowPaint);
+ canvas.restoreToCount(saveCount);
+ }
+
+ saveCount = canvas.save();
+ canvas.translate(mShadowSize + mRadius, mShadowSize + mRadius);
+ if(mShadowSize > 0)
+ canvas.drawPath(mGlowPath, mGlowPaint);
+ mTempRect.set(-mRadius, -mRadius, mRadius, mRadius);
+ if(!isRunning())
+ mPaint.setColor(mCurColor);
+ else
+ mPaint.setColor(ColorUtil.getMiddleColor(mPrevColor, mCurColor, mAnimProgress));
+ canvas.drawOval(mTempRect, mPaint);
+ canvas.restoreToCount(saveCount);
+ }
+
+ @Override
+ public void setAlpha(int alpha) {
+ mShadowPaint.setAlpha(alpha);
+ mPaint.setAlpha(alpha);
+ }
+
+ @Override
+ public void setColorFilter(ColorFilter cf) {
+ mShadowPaint.setColorFilter(cf);
+ mPaint.setColorFilter(cf);
+ }
+
+ @Override
+ public int getOpacity() {
+ return PixelFormat.TRANSLUCENT;
+ }
+
+ @Override
+ public boolean isStateful() {
+ return true;
+ }
+
+ @Override
+ protected boolean onStateChange(int[] state) {
+ mEnable = ViewUtil.hasState(state, android.R.attr.state_enabled);
+ int color = mColorStateList.getColorForState(state, mCurColor);
+
+ if(mCurColor != color){
+ if(!mInEditMode && mAnimEnable && mEnable && mAnimDuration > 0){
+ mPrevColor = isRunning() ? mPrevColor : mCurColor;
+ mCurColor = color;
+ start();
+ }
+ else{
+ mPrevColor = color;
+ mCurColor = color;
+ invalidateSelf();
+ }
+ return true;
+ }
+ else if(!isRunning())
+ mPrevColor = color;
+
+ return false;
+ }
+
+ @Override
+ public void jumpToCurrentState() {
+ super.jumpToCurrentState();
+ stop();
+ }
+
+ private void resetAnimation(){
+ mStartTime = SystemClock.uptimeMillis();
+ mAnimProgress = 0f;
+ }
+
+ @Override
+ public void start() {
+ resetAnimation();
+ scheduleSelf(mUpdater, SystemClock.uptimeMillis() + ViewUtil.FRAME_DURATION);
+ invalidateSelf();
+ }
+
+ @Override
+ public void stop() {
+ mRunning = false;
+ unscheduleSelf(mUpdater);
+ invalidateSelf();
+ }
+
+ @Override
+ public boolean isRunning() {
+ return mRunning;
+ }
+
+ @Override
+ public void scheduleSelf(Runnable what, long when) {
+ mRunning = true;
+ super.scheduleSelf(what, when);
+ }
+
+ private final Runnable mUpdater = new Runnable() {
+
+ @Override
+ public void run() {
+ update();
+ }
+
+ };
+
+ private void update(){
+ long curTime = SystemClock.uptimeMillis();
+ mAnimProgress = Math.min(1f, (float)(curTime - mStartTime) / mAnimDuration);
+
+ if(mAnimProgress == 1f)
+ mRunning = false;
+
+ if(isRunning())
+ scheduleSelf(mUpdater, SystemClock.uptimeMillis() + ViewUtil.FRAME_DURATION);
+
+ invalidateSelf();
+ }
+
+}
diff --git a/lib/src/main/java/com/rey/material/drawable/RippleDrawable.java b/lib/src/main/java/com/rey/material/drawable/RippleDrawable.java
index 1f582eaa..f18956cb 100644
--- a/lib/src/main/java/com/rey/material/drawable/RippleDrawable.java
+++ b/lib/src/main/java/com/rey/material/drawable/RippleDrawable.java
@@ -19,6 +19,7 @@
import android.graphics.drawable.Drawable;
import android.os.SystemClock;
import android.util.AttributeSet;
+import android.util.Log;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.View;
@@ -128,6 +129,10 @@ public void setBackgroundDrawable(Drawable backgroundDrawable){
mBackgroundDrawable.setBounds(getBounds());
}
+ public Drawable getBackgroundDrawable(){
+ return mBackgroundDrawable;
+ }
+
public int getDelayClickType(){
return mDelayClickType;
}
@@ -177,16 +182,18 @@ else if(mState == STATE_RELEASE)
private void setRippleState(int state){
if(mState != state){
+ //fix bug incorrect state switch
+ if(mState == STATE_OUT && state != STATE_PRESS)
+ return;
+
+// Log.v(RippleDrawable.class.getSimpleName(), "state: " + mState + " " + state);
+
mState = state;
-
- if(mState != STATE_OUT){
- if(mState != STATE_HOVER)
- start();
- else
- stop();
- }
- else
- stop();
+
+ if(mState == STATE_OUT || mState == STATE_HOVER)
+ stop();
+ else
+ start();
}
}
@@ -296,7 +303,9 @@ private int getMaxRippleRadius(float x, float y){
}
@Override
- public boolean onTouch(View v, MotionEvent event) {
+ public boolean onTouch(View v, MotionEvent event) {
+// Log.v(RippleDrawable.class.getSimpleName(), "touch: " + event.getAction() + " " + mState);
+
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
@@ -340,21 +349,17 @@ private void resetAnimation(){
}
@Override
- public void start() {
+ public void start() {
if(isRunning())
return;
resetAnimation();
-
scheduleSelf(mUpdater, SystemClock.uptimeMillis() + ViewUtil.FRAME_DURATION);
invalidateSelf();
}
@Override
- public void stop() {
- if(!isRunning())
- return;
-
+ public void stop() {
mRunning = false;
unscheduleSelf(mUpdater);
invalidateSelf();
@@ -362,7 +367,7 @@ public void stop() {
@Override
public boolean isRunning() {
- return mRunning;
+ return mState != STATE_OUT && mState != STATE_HOVER && mRunning;
}
@Override
@@ -402,7 +407,6 @@ private void updateTouch(){
mStartTime = SystemClock.uptimeMillis();
setRippleState(mState == STATE_PRESS ? STATE_HOVER : STATE_RELEASE);
}
-
}
else{
float backgroundProgress = Math.min(1f, (float)(SystemClock.uptimeMillis() - mStartTime) / mBackgroundAnimDuration);
diff --git a/lib/src/main/java/com/rey/material/drawable/ThemeDrawable.java b/lib/src/main/java/com/rey/material/drawable/ThemeDrawable.java
new file mode 100644
index 00000000..9fd46850
--- /dev/null
+++ b/lib/src/main/java/com/rey/material/drawable/ThemeDrawable.java
@@ -0,0 +1,41 @@
+package com.rey.material.drawable;
+
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LevelListDrawable;
+
+import com.rey.material.app.ThemeManager;
+
+/**
+ * Created by Rey on 5/27/2015.
+ */
+public class ThemeDrawable extends LevelListDrawable implements ThemeManager.OnThemeChangedListener {
+ private int mStyleId;
+
+ public ThemeDrawable(int styleId) {
+ mStyleId = styleId;
+
+ if(mStyleId != 0) {
+ ThemeManager.getInstance().registerOnThemeChangedListener(this);
+ initDrawables();
+ }
+ }
+
+ private void initDrawables(){
+ ThemeManager themeManager = ThemeManager.getInstance();
+ int count = themeManager.getThemeCount();
+
+ for(int i = 0; i < count; i++){
+ Drawable drawable = themeManager.getContext().getResources().getDrawable(themeManager.getStyle(mStyleId, i));
+ addLevel(i, i, drawable);
+ }
+
+ setLevel(themeManager.getCurrentTheme());
+ }
+
+ @Override
+ public void onThemeChanged(ThemeManager.OnThemeChangedEvent event) {
+ if(getLevel() != event.theme)
+ setLevel(event.theme);
+ }
+
+}
diff --git a/lib/src/main/java/com/rey/material/util/ViewUtil.java b/lib/src/main/java/com/rey/material/util/ViewUtil.java
index c691f57e..2fddfc48 100644
--- a/lib/src/main/java/com/rey/material/util/ViewUtil.java
+++ b/lib/src/main/java/com/rey/material/util/ViewUtil.java
@@ -3,9 +3,19 @@
import java.util.concurrent.atomic.AtomicInteger;
import android.annotation.SuppressLint;
+import android.content.res.TypedArray;
+import android.graphics.PorterDuff;
+import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.os.Build;
+import android.util.AttributeSet;
+import android.util.TypedValue;
import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AutoCompleteTextView;
+import android.widget.TextView;
+
+import com.rey.material.R;
public class ViewUtil {
@@ -47,4 +57,609 @@ public static void setBackground(View v, Drawable drawable){
else
v.setBackgroundDrawable(drawable);
}
+
+ /**
+ * Apply any View style attributes to a view.
+ * @param v The view is applied.
+ * @param resId The style resourceId.
+ */
+ public static void applyStyle(View v, int resId){
+ applyStyle(v, null, 0, resId);
+ }
+
+ /**
+ * Apply any View style attributes to a view.
+ * @param v The view is applied.
+ * @param attrs
+ * @param defStyleAttr
+ * @param defStyleRes
+ */
+ public static void applyStyle(View v, AttributeSet attrs, int defStyleAttr, int defStyleRes){
+ TypedArray a = v.getContext().obtainStyledAttributes(attrs, R.styleable.View, defStyleAttr, defStyleRes);
+
+ int leftPadding = -1;
+ int topPadding = -1;
+ int rightPadding = -1;
+ int bottomPadding = -1;
+ int startPadding = Integer.MIN_VALUE;
+ int endPadding = Integer.MIN_VALUE;
+ int padding = -1;
+
+ boolean startPaddingDefined = false;
+ boolean endPaddingDefined = false;
+ boolean leftPaddingDefined = false;
+ boolean rightPaddingDefined = false;
+
+ for(int i = 0, count = a.getIndexCount(); i < count; i++){
+ int attr = a.getIndex(i);
+ if(attr == R.styleable.View_android_background) {
+ Drawable bg = a.getDrawable(attr);
+ ViewUtil.setBackground(v, bg);
+ }
+ else if(attr == R.styleable.View_android_backgroundTint){
+ if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
+ v.setBackgroundTintList(a.getColorStateList(attr));
+ }
+ else if(attr == R.styleable.View_android_backgroundTintMode){
+ if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
+ int value = a.getInt(attr, 3);
+ switch (value){
+ case 3:
+ v.setBackgroundTintMode(PorterDuff.Mode.SRC_OVER);
+ break;
+ case 5:
+ v.setBackgroundTintMode(PorterDuff.Mode.SRC_IN);
+ break;
+ case 9:
+ v.setBackgroundTintMode(PorterDuff.Mode.SRC_ATOP);
+ break;
+ case 14:
+ v.setBackgroundTintMode(PorterDuff.Mode.MULTIPLY);
+ break;
+ case 15:
+ v.setBackgroundTintMode(PorterDuff.Mode.SCREEN);
+ break;
+ case 16:
+ v.setBackgroundTintMode(PorterDuff.Mode.ADD);
+ break;
+ }
+ }
+ }
+ else if(attr == R.styleable.View_android_elevation){
+ if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
+ v.setElevation(a.getDimensionPixelOffset(attr, 0));
+ }
+ else if(attr == R.styleable.View_android_padding) {
+ padding = a.getDimensionPixelSize(attr, -1);
+ leftPaddingDefined = true;
+ rightPaddingDefined = true;
+ }
+ else if(attr == R.styleable.View_android_paddingLeft) {
+ leftPadding = a.getDimensionPixelSize(attr, -1);
+ leftPaddingDefined = true;
+ }
+ else if(attr == R.styleable.View_android_paddingTop)
+ topPadding = a.getDimensionPixelSize(attr, -1);
+ else if(attr == R.styleable.View_android_paddingRight) {
+ rightPadding = a.getDimensionPixelSize(attr, -1);
+ rightPaddingDefined = true;
+ }
+ else if(attr == R.styleable.View_android_paddingBottom)
+ bottomPadding = a.getDimensionPixelSize(attr, -1);
+ else if(attr == R.styleable.View_android_paddingStart) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ startPadding = a.getDimensionPixelSize(attr, Integer.MIN_VALUE);
+ startPaddingDefined = (startPadding != Integer.MIN_VALUE);
+ }
+ }
+ else if(attr == R.styleable.View_android_paddingEnd) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ endPadding = a.getDimensionPixelSize(attr, Integer.MIN_VALUE);
+ endPaddingDefined = (endPadding != Integer.MIN_VALUE);
+ }
+ }
+ else if(attr == R.styleable.View_android_fadeScrollbars)
+ v.setScrollbarFadingEnabled(a.getBoolean(attr, true));
+ else if(attr == R.styleable.View_android_fadingEdgeLength)
+ v.setFadingEdgeLength(a.getDimensionPixelOffset(attr, 0));
+ else if(attr == R.styleable.View_android_minHeight)
+ v.setMinimumHeight(a.getDimensionPixelSize(attr, 0));
+ else if(attr == R.styleable.View_android_minWidth)
+ v.setMinimumWidth(a.getDimensionPixelSize(attr, 0));
+ else if(attr == R.styleable.View_android_requiresFadingEdge)
+ v.setVerticalFadingEdgeEnabled(a.getBoolean(attr, true));
+ else if(attr == R.styleable.View_android_scrollbarDefaultDelayBeforeFade) {
+ if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN)
+ v.setScrollBarDefaultDelayBeforeFade(a.getInteger(attr, 0));
+ }
+ else if(attr == R.styleable.View_android_scrollbarFadeDuration) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN)
+ v.setScrollBarFadeDuration(a.getInteger(attr, 0));
+ }
+ else if(attr == R.styleable.View_android_scrollbarSize) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN)
+ v.setScrollBarSize(a.getDimensionPixelSize(attr, 0));
+ }
+ else if(attr == R.styleable.View_android_scrollbarStyle) {
+ int value = a.getInteger(attr, 0);
+ switch (value){
+ case 0x0:
+ v.setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY);
+ break;
+ case 0x01000000:
+ v.setScrollBarStyle(View.SCROLLBARS_INSIDE_INSET);
+ break;
+ case 0x02000000:
+ v.setScrollBarStyle(View.SCROLLBARS_OUTSIDE_OVERLAY);
+ break;
+ case 0x03000000:
+ v.setScrollBarStyle(View.SCROLLBARS_OUTSIDE_INSET);
+ break;
+ }
+ }
+ else if(attr == R.styleable.View_android_soundEffectsEnabled)
+ v.setSoundEffectsEnabled(a.getBoolean(attr, true));
+ else if(attr == R.styleable.View_android_textAlignment){
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ int value = a.getInteger(attr, 0);
+ switch (value){
+ case 0:
+ v.setTextAlignment(View.TEXT_ALIGNMENT_INHERIT);
+ break;
+ case 1:
+ v.setTextAlignment(View.TEXT_ALIGNMENT_GRAVITY);
+ break;
+ case 2:
+ v.setTextAlignment(View.TEXT_ALIGNMENT_TEXT_START);
+ break;
+ case 3:
+ v.setTextAlignment(View.TEXT_ALIGNMENT_TEXT_END);
+ break;
+ case 4:
+ v.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
+ break;
+ case 5:
+ v.setTextAlignment(View.TEXT_ALIGNMENT_VIEW_START);
+ break;
+ case 6:
+ v.setTextAlignment(View.TEXT_ALIGNMENT_VIEW_END);
+ break;
+ }
+ }
+ }
+ else if(attr == R.styleable.View_android_textDirection){
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ int value = a.getInteger(attr, 0);
+ switch (value){
+ case 0:
+ v.setTextDirection(View.TEXT_DIRECTION_INHERIT);
+ break;
+ case 1:
+ v.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG);
+ break;
+ case 2:
+ v.setTextDirection(View.TEXT_DIRECTION_ANY_RTL);
+ break;
+ case 3:
+ v.setTextDirection(View.TEXT_DIRECTION_LTR);
+ break;
+ case 4:
+ v.setTextDirection(View.TEXT_DIRECTION_RTL);
+ break;
+ case 5:
+ v.setTextDirection(View.TEXT_DIRECTION_LOCALE);
+ break;
+ }
+ }
+ }
+ else if(attr == R.styleable.View_android_visibility){
+ int value = a.getInteger(attr, 0);
+ switch (value){
+ case 0:
+ v.setVisibility(View.VISIBLE);
+ break;
+ case 1:
+ v.setVisibility(View.INVISIBLE);
+ break;
+ case 2:
+ v.setVisibility(View.GONE);
+ break;
+ }
+ }
+ else if(attr == R.styleable.View_android_layoutDirection){
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ int value = a.getInteger(attr, 0);
+ switch (value){
+ case 0:
+ v.setLayoutDirection(View.LAYOUT_DIRECTION_LTR);
+ break;
+ case 1:
+ v.setLayoutDirection(View.LAYOUT_DIRECTION_RTL);
+ break;
+ case 2:
+ v.setLayoutDirection(View.LAYOUT_DIRECTION_INHERIT);
+ break;
+ case 3:
+ v.setLayoutDirection(View.LAYOUT_DIRECTION_LOCALE);
+ break;
+ }
+ }
+ }
+ }
+
+ if (padding >= 0)
+ v.setPadding(padding, padding, padding, padding);
+ else if(Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1){
+ if(startPaddingDefined)
+ leftPadding = startPadding;
+ if(endPaddingDefined)
+ rightPadding = endPadding;
+
+ v.setPadding(leftPadding >= 0 ? leftPadding : v.getPaddingLeft(),
+ topPadding >= 0 ? topPadding : v.getPaddingTop(),
+ rightPadding >= 0 ? rightPadding : v.getPaddingRight(),
+ bottomPadding >= 0 ? bottomPadding : v.getPaddingBottom());
+ }
+ else{
+ if(leftPaddingDefined || rightPaddingDefined)
+ v.setPadding(leftPaddingDefined ? leftPadding : v.getPaddingLeft(),
+ topPadding >= 0 ? topPadding : v.getPaddingTop(),
+ rightPaddingDefined ? rightPadding : v.getPaddingRight(),
+ bottomPadding >= 0 ? bottomPadding : v.getPaddingBottom());
+
+ if(startPaddingDefined || endPaddingDefined)
+ v.setPaddingRelative(startPaddingDefined ? startPadding : v.getPaddingStart(),
+ topPadding >= 0 ? topPadding : v.getPaddingTop(),
+ endPaddingDefined ? endPadding : v.getPaddingEnd(),
+ bottomPadding >= 0 ? bottomPadding : v.getPaddingBottom());
+ }
+
+ a.recycle();
+
+ if(v instanceof TextView)
+ applyStyle((TextView)v, attrs, defStyleAttr, defStyleRes);
+ }
+
+ public static void applyFont(TextView v, AttributeSet attrs, int defStyleAttr, int defStyleRes){
+ TypedArray a = v.getContext().obtainStyledAttributes(attrs, new int[]{R.attr.tv_fontFamily}, defStyleAttr, defStyleRes);
+ String fontFamily = a.getString(0);
+
+ if(fontFamily != null){
+ Typeface typeface = TypefaceUtil.load(v.getContext(), fontFamily, 0);
+ v.setTypeface(typeface);
+ }
+
+ a.recycle();
+ }
+
+ /**
+ * Apply any TextView style attributes to a view.
+ * @param v
+ * @param attrs
+ * @param defStyleAttr
+ * @param defStyleRes
+ */
+ private static void applyStyle(TextView v, AttributeSet attrs, int defStyleAttr, int defStyleRes){
+ String fontFamily = null;
+ int typefaceIndex = -1;
+ int styleIndex = -1;
+ int shadowColor = 0;
+ float dx = 0, dy = 0, r = 0;
+
+ Drawable drawableLeft = null, drawableTop = null, drawableRight = null,
+ drawableBottom = null, drawableStart = null, drawableEnd = null;
+ boolean drawableDefined = false;
+ boolean drawableRelativeDefined = false;
+
+ /*
+ * Look the appearance up without checking first if it exists because
+ * almost every TextView has one and it greatly simplifies the logic
+ * to be able to parse the appearance first and then let specific tags
+ * for this View override it.
+ */
+ TypedArray a = v.getContext().obtainStyledAttributes(attrs, R.styleable.TextViewAppearance, defStyleAttr, defStyleRes);
+ TypedArray appearance = null;
+ int ap = a.getResourceId(R.styleable.TextViewAppearance_android_textAppearance, -1);
+ a.recycle();
+
+ if (ap != -1)
+ appearance = v.getContext().obtainStyledAttributes(ap, R.styleable.TextAppearance);
+
+ if (appearance != null) {
+ int n = appearance.getIndexCount();
+ for (int i = 0; i < n; i++) {
+ int attr = appearance.getIndex(i);
+
+ if (attr == R.styleable.TextAppearance_android_textColorHighlight) {
+ v.setHighlightColor(appearance.getColor(attr, 0));
+
+ } else if (attr == R.styleable.TextAppearance_android_textColor) {
+ v.setTextColor(appearance.getColorStateList(attr));
+
+ } else if (attr == R.styleable.TextAppearance_android_textColorHint) {
+ v.setHintTextColor(appearance.getColorStateList(attr));
+
+ } else if (attr == R.styleable.TextAppearance_android_textColorLink) {
+ v.setLinkTextColor(appearance.getColorStateList(attr));
+
+ } else if (attr == R.styleable.TextAppearance_android_textSize) {
+ v.setTextSize(TypedValue.COMPLEX_UNIT_PX, appearance.getDimensionPixelSize(attr, 0));
+
+ } else if (attr == R.styleable.TextAppearance_android_typeface) {
+ typefaceIndex = appearance.getInt(attr, -1);
+
+ } else if (attr == R.styleable.TextAppearance_android_fontFamily) {
+ fontFamily = appearance.getString(attr);
+
+ } else if (attr == R.styleable.TextAppearance_tv_fontFamily) {
+ fontFamily = appearance.getString(attr);
+
+ } else if (attr == R.styleable.TextAppearance_android_textStyle) {
+ styleIndex = appearance.getInt(attr, -1);
+
+ } else if (attr == R.styleable.TextAppearance_android_textAllCaps) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH)
+ v.setAllCaps(appearance.getBoolean(attr, false));
+
+ } else if (attr == R.styleable.TextAppearance_android_shadowColor) {
+ shadowColor = appearance.getInt(attr, 0);
+
+ } else if (attr == R.styleable.TextAppearance_android_shadowDx) {
+ dx = appearance.getFloat(attr, 0);
+
+ } else if (attr == R.styleable.TextAppearance_android_shadowDy) {
+ dy = appearance.getFloat(attr, 0);
+
+ } else if (attr == R.styleable.TextAppearance_android_shadowRadius) {
+ r = appearance.getFloat(attr, 0);
+
+ } else if (attr == R.styleable.TextAppearance_android_elegantTextHeight) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
+ v.setElegantTextHeight(appearance.getBoolean(attr, false));
+
+ } else if (attr == R.styleable.TextAppearance_android_letterSpacing) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
+ v.setLetterSpacing(appearance.getFloat(attr, 0));
+
+ } else if (attr == R.styleable.TextAppearance_android_fontFeatureSettings) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
+ v.setFontFeatureSettings(appearance.getString(attr));
+
+ }
+ }
+
+ appearance.recycle();
+ }
+
+ a = v.getContext().obtainStyledAttributes(attrs, R.styleable.TextView, defStyleAttr, defStyleRes);
+
+ int n = a.getIndexCount();
+ for (int i = 0; i < n; i++) {
+ int attr = a.getIndex(i);
+
+ if (attr == R.styleable.TextView_android_drawableLeft) {
+ drawableLeft = a.getDrawable(attr);
+ drawableDefined = true;
+
+ } else if (attr == R.styleable.TextView_android_drawableTop) {
+ drawableTop = a.getDrawable(attr);
+ drawableDefined = true;
+
+ } else if (attr == R.styleable.TextView_android_drawableRight) {
+ drawableRight = a.getDrawable(attr);
+ drawableDefined = true;
+
+ } else if (attr == R.styleable.TextView_android_drawableBottom) {
+ drawableBottom = a.getDrawable(attr);
+ drawableDefined = true;
+
+ } else if (attr == R.styleable.TextView_android_drawableStart) {
+ drawableStart = a.getDrawable(attr);
+ drawableRelativeDefined = true;
+
+ } else if (attr == R.styleable.TextView_android_drawableEnd) {
+ drawableEnd = a.getDrawable(attr);
+ drawableRelativeDefined = true;
+
+ } else if (attr == R.styleable.TextView_android_drawablePadding) {
+ v.setCompoundDrawablePadding(a.getDimensionPixelSize(attr, 0));
+
+ } else if (attr == R.styleable.TextView_android_maxLines) {
+ v.setMaxLines(a.getInt(attr, -1));
+
+ } else if (attr == R.styleable.TextView_android_maxHeight) {
+ v.setMaxHeight(a.getDimensionPixelSize(attr, -1));
+
+ } else if (attr == R.styleable.TextView_android_lines) {
+ v.setLines(a.getInt(attr, -1));
+
+ } else if (attr == R.styleable.TextView_android_height) {
+ v.setHeight(a.getDimensionPixelSize(attr, -1));
+
+ } else if (attr == R.styleable.TextView_android_minLines) {
+ v.setMinLines(a.getInt(attr, -1));
+
+ } else if (attr == R.styleable.TextView_android_minHeight) {
+ v.setMinHeight(a.getDimensionPixelSize(attr, -1));
+
+ } else if (attr == R.styleable.TextView_android_maxEms) {
+ v.setMaxEms(a.getInt(attr, -1));
+
+ } else if (attr == R.styleable.TextView_android_maxWidth) {
+ v.setMaxWidth(a.getDimensionPixelSize(attr, -1));
+
+ } else if (attr == R.styleable.TextView_android_ems) {
+ v.setEms(a.getInt(attr, -1));
+
+ } else if (attr == R.styleable.TextView_android_width) {
+ v.setWidth(a.getDimensionPixelSize(attr, -1));
+
+ } else if (attr == R.styleable.TextView_android_minEms) {
+ v.setMinEms(a.getInt(attr, -1));
+
+ } else if (attr == R.styleable.TextView_android_minWidth) {
+ v.setMinWidth(a.getDimensionPixelSize(attr, -1));
+
+ } else if (attr == R.styleable.TextView_android_gravity) {
+ v.setGravity(a.getInt(attr, -1));
+
+ } else if (attr == R.styleable.TextView_android_scrollHorizontally) {
+ v.setHorizontallyScrolling(a.getBoolean(attr, false));
+
+ } else if (attr == R.styleable.TextView_android_includeFontPadding) {
+ v.setIncludeFontPadding(a.getBoolean(attr, true));
+
+ } else if (attr == R.styleable.TextView_android_cursorVisible) {
+ v.setCursorVisible(a.getBoolean(attr, true));
+
+ } else if (attr == R.styleable.TextView_android_textScaleX) {
+ v.setTextScaleX(a.getFloat(attr, 1.0f));
+
+ } else if (attr == R.styleable.TextView_android_shadowColor) {
+ shadowColor = a.getInt(attr, 0);
+
+ } else if (attr == R.styleable.TextView_android_shadowDx) {
+ dx = a.getFloat(attr, 0);
+
+ } else if (attr == R.styleable.TextView_android_shadowDy) {
+ dy = a.getFloat(attr, 0);
+
+ } else if (attr == R.styleable.TextView_android_shadowRadius) {
+ r = a.getFloat(attr, 0);
+
+ } else if (attr == R.styleable.TextView_android_textColorHighlight) {
+ v.setHighlightColor(a.getColor(attr, 0));
+
+ } else if (attr == R.styleable.TextView_android_textColor) {
+ v.setTextColor(a.getColorStateList(attr));
+
+ } else if (attr == R.styleable.TextView_android_textColorHint) {
+ v.setHintTextColor(a.getColorStateList(attr));
+
+ } else if (attr == R.styleable.TextView_android_textColorLink) {
+ v.setLinkTextColor(a.getColorStateList(attr));
+
+ } else if (attr == R.styleable.TextView_android_textSize) {
+ v.setTextSize(TypedValue.COMPLEX_UNIT_PX, a.getDimensionPixelSize(attr, 0));
+
+ } else if (attr == R.styleable.TextView_android_typeface) {
+ typefaceIndex = a.getInt(attr, -1);
+
+ } else if (attr == R.styleable.TextView_android_textStyle) {
+ styleIndex = a.getInt(attr, -1);
+
+ } else if (attr == R.styleable.TextView_android_fontFamily) {
+ fontFamily = a.getString(attr);
+
+ } else if (attr == R.styleable.TextView_tv_fontFamily) {
+ fontFamily = a.getString(attr);
+
+ } else if (attr == R.styleable.TextView_android_textAllCaps) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH)
+ v.setAllCaps(a.getBoolean(attr, false));
+
+ } else if (attr == R.styleable.TextView_android_elegantTextHeight) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
+ v.setElegantTextHeight(a.getBoolean(attr, false));
+
+ } else if (attr == R.styleable.TextView_android_letterSpacing) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
+ v.setLetterSpacing(a.getFloat(attr, 0));
+
+ } else if (attr == R.styleable.TextView_android_fontFeatureSettings) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
+ v.setFontFeatureSettings(a.getString(attr));
+
+ }
+ }
+ a.recycle();
+
+ if (shadowColor != 0)
+ v.setShadowLayer(r, dx, dy, shadowColor);
+
+ if(drawableDefined) {
+ Drawable[] drawables = v.getCompoundDrawables();
+ if (drawableStart != null)
+ drawables[0] = drawableStart;
+ else if (drawableLeft != null)
+ drawables[0] = drawableLeft;
+ if (drawableTop != null)
+ drawables[1] = drawableTop;
+ if (drawableEnd != null)
+ drawables[2] = drawableEnd;
+ else if (drawableRight != null)
+ drawables[2] = drawableRight;
+ if (drawableBottom != null)
+ drawables[3] = drawableBottom;
+ v.setCompoundDrawablesWithIntrinsicBounds(drawables[0], drawables[1], drawables[2], drawables[3]);
+ }
+
+ if(drawableRelativeDefined && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1){
+ Drawable[] drawables = v.getCompoundDrawablesRelative();
+ if (drawableStart != null)
+ drawables[0] = drawableStart;
+ if (drawableEnd != null)
+ drawables[2] = drawableEnd;
+ v.setCompoundDrawablesRelativeWithIntrinsicBounds(drawables[0], drawables[1], drawables[2], drawables[3]);
+ }
+
+ Typeface tf = null;
+ if (fontFamily != null) {
+ tf = TypefaceUtil.load(v.getContext(), fontFamily, styleIndex);
+ if (tf != null)
+ v.setTypeface(tf);
+ }
+ if(tf != null) {
+ switch (typefaceIndex) {
+ case 1:
+ tf = Typeface.SANS_SERIF;
+ break;
+ case 2:
+ tf = Typeface.SERIF;
+ break;
+ case 3:
+ tf = Typeface.MONOSPACE;
+ break;
+ }
+ v.setTypeface(tf, styleIndex);
+ }
+
+ if(v instanceof AutoCompleteTextView)
+ applyStyle((AutoCompleteTextView)v, attrs, defStyleAttr, defStyleRes);
+ }
+
+ /**
+ * Apply any AutoCompleteTextView style attributes to a view.
+ * @param v
+ * @param attrs
+ * @param defStyleAttr
+ * @param defStyleRes
+ */
+ private static void applyStyle(AutoCompleteTextView v, AttributeSet attrs, int defStyleAttr, int defStyleRes){
+ TypedArray a = v.getContext().obtainStyledAttributes(attrs, R.styleable.AutoCompleteTextView, defStyleAttr, defStyleRes);
+
+ int n = a.getIndexCount();
+ for (int i = 0; i < n; i++) {
+ int attr = a.getIndex(i);
+
+ if(attr == R.styleable.AutoCompleteTextView_android_completionHint)
+ v.setCompletionHint(a.getString(attr));
+ else if(attr == R.styleable.AutoCompleteTextView_android_completionThreshold)
+ v.setThreshold(a.getInteger(attr, 0));
+ else if(attr == R.styleable.AutoCompleteTextView_android_dropDownAnchor)
+ v.setDropDownAnchor(a.getResourceId(attr, 0));
+ else if(attr == R.styleable.AutoCompleteTextView_android_dropDownHeight)
+ v.setDropDownHeight(a.getLayoutDimension(attr, ViewGroup.LayoutParams.WRAP_CONTENT));
+ else if(attr == R.styleable.AutoCompleteTextView_android_dropDownWidth)
+ v.setDropDownWidth(a.getLayoutDimension(attr, ViewGroup.LayoutParams.WRAP_CONTENT));
+ else if(attr == R.styleable.AutoCompleteTextView_android_dropDownHorizontalOffset)
+ v.setDropDownHorizontalOffset(a.getDimensionPixelSize(attr, 0));
+ else if(attr == R.styleable.AutoCompleteTextView_android_dropDownVerticalOffset)
+ v.setDropDownVerticalOffset(a.getDimensionPixelSize(attr, 0));
+ else if(attr == R.styleable.AutoCompleteTextView_android_popupBackground)
+ v.setDropDownBackgroundDrawable(a.getDrawable(attr));
+ }
+ a.recycle();
+ }
+
}
diff --git a/lib/src/main/java/com/rey/material/widget/Button.java b/lib/src/main/java/com/rey/material/widget/Button.java
index 11e62f95..41ae0e23 100644
--- a/lib/src/main/java/com/rey/material/widget/Button.java
+++ b/lib/src/main/java/com/rey/material/widget/Button.java
@@ -1,20 +1,23 @@
package com.rey.material.widget;
import android.content.Context;
+import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.support.annotation.NonNull;
-import android.support.v7.internal.widget.TintManager;
import android.util.AttributeSet;
import android.view.MotionEvent;
+import com.rey.material.app.ThemeManager;
import com.rey.material.drawable.RippleDrawable;
+import com.rey.material.util.ViewUtil;
-import java.lang.reflect.Field;
-
-public class Button extends android.widget.Button {
+public class Button extends android.widget.Button implements ThemeManager.OnThemeChangedListener{
private RippleManager mRippleManager;
+ protected int mStyleId;
+ protected int mCurrentStyle = ThemeManager.THEME_UNDEFINED;
+
public Button(Context context) {
super(context);
@@ -40,17 +43,46 @@ public Button(Context context, AttributeSet attrs, int defStyleAttr, int defStyl
}
private void init(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){
+ ViewUtil.applyFont(this, attrs, defStyleAttr, defStyleRes);
applyStyle(context, attrs, defStyleAttr, defStyleRes);
+ mStyleId = ThemeManager.getStyleId(context, attrs, defStyleAttr, defStyleRes);
}
public void applyStyle(int resId){
+ ViewUtil.applyStyle(this, resId);
applyStyle(getContext(), null, 0, resId);
}
- private void applyStyle(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){
+ protected void applyStyle(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){
getRippleManager().onCreate(this, context, attrs, defStyleAttr, defStyleRes);
}
+ @Override
+ public void onThemeChanged(ThemeManager.OnThemeChangedEvent event) {
+ int style = ThemeManager.getInstance().getCurrentStyle(mStyleId);
+ if(mCurrentStyle != style){
+ mCurrentStyle = style;
+ applyStyle(mCurrentStyle);
+ }
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ if(mStyleId != 0) {
+ ThemeManager.getInstance().registerOnThemeChangedListener(this);
+ onThemeChanged(null);
+ }
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ mRippleManager.cancelRipple(this);
+ if(mStyleId != 0)
+ ThemeManager.getInstance().unregisterOnThemeChangedListener(this);
+ }
+
@Override
public void setBackgroundDrawable(Drawable drawable) {
Drawable background = getBackground();
diff --git a/lib/src/main/java/com/rey/material/widget/CheckBox.java b/lib/src/main/java/com/rey/material/widget/CheckBox.java
index 90fa9ea3..3153c603 100644
--- a/lib/src/main/java/com/rey/material/widget/CheckBox.java
+++ b/lib/src/main/java/com/rey/material/widget/CheckBox.java
@@ -9,37 +9,24 @@ public class CheckBox extends CompoundButton {
public CheckBox(Context context) {
super(context);
-
- init(context, null, 0, 0);
}
public CheckBox(Context context, AttributeSet attrs) {
super(context, attrs);
-
- init(context, attrs, 0, 0);
}
public CheckBox(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
-
- init(context, attrs, defStyleAttr, 0);
}
public CheckBox(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr);
-
- init(context, attrs, defStyleAttr, defStyleRes);
}
- private void init(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){
- applyStyle(context, attrs, defStyleAttr, defStyleRes);
- }
-
- public void applyStyle(int resId){
- applyStyle(getContext(), null, 0, resId);
- }
+ @Override
+ protected void applyStyle(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){
+ super.applyStyle(context, attrs, defStyleAttr, defStyleRes);
- private void applyStyle(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){
CheckBoxDrawable drawable = new CheckBoxDrawable.Builder(context, attrs, defStyleAttr, defStyleRes).build();
drawable.setInEditMode(isInEditMode());
drawable.setAnimEnable(false);
diff --git a/lib/src/main/java/com/rey/material/widget/CheckedTextView.java b/lib/src/main/java/com/rey/material/widget/CheckedTextView.java
index e1d845bd..eedab676 100644
--- a/lib/src/main/java/com/rey/material/widget/CheckedTextView.java
+++ b/lib/src/main/java/com/rey/material/widget/CheckedTextView.java
@@ -6,11 +6,15 @@
import android.util.AttributeSet;
import android.view.MotionEvent;
+import com.rey.material.app.ThemeManager;
import com.rey.material.drawable.RippleDrawable;
+import com.rey.material.util.ViewUtil;
-public class CheckedTextView extends android.widget.CheckedTextView {
+public class CheckedTextView extends android.widget.CheckedTextView implements ThemeManager.OnThemeChangedListener {
private RippleManager mRippleManager;
+ protected int mStyleId;
+ protected int mCurrentStyle = ThemeManager.THEME_UNDEFINED;
public CheckedTextView(Context context) {
super(context);
@@ -38,16 +42,45 @@ public CheckedTextView(Context context, AttributeSet attrs, int defStyleAttr, in
private void init(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){
applyStyle(context, attrs, defStyleAttr, defStyleRes);
+
+ mStyleId = ThemeManager.getStyleId(context, attrs, defStyleAttr, defStyleRes);
}
public void applyStyle(int resId){
+ ViewUtil.applyStyle(this, resId);
applyStyle(getContext(), null, 0, resId);
}
- private void applyStyle(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){
+ protected void applyStyle(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){
getRippleManager().onCreate(this, context, attrs, defStyleAttr, defStyleRes);
}
+ @Override
+ public void onThemeChanged(ThemeManager.OnThemeChangedEvent event) {
+ int style = ThemeManager.getInstance().getCurrentStyle(mStyleId);
+ if(mCurrentStyle != style){
+ mCurrentStyle = style;
+ applyStyle(mCurrentStyle);
+ }
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ if(mStyleId != 0) {
+ ThemeManager.getInstance().registerOnThemeChangedListener(this);
+ onThemeChanged(null);
+ }
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ mRippleManager.cancelRipple(this);
+ if(mStyleId != 0)
+ ThemeManager.getInstance().unregisterOnThemeChangedListener(this);
+ }
+
@Override
public void setBackgroundDrawable(Drawable drawable) {
Drawable background = getBackground();
diff --git a/lib/src/main/java/com/rey/material/widget/CompoundButton.java b/lib/src/main/java/com/rey/material/widget/CompoundButton.java
index 68fb15e9..f4ec5a4d 100644
--- a/lib/src/main/java/com/rey/material/widget/CompoundButton.java
+++ b/lib/src/main/java/com/rey/material/widget/CompoundButton.java
@@ -9,13 +9,18 @@
import android.util.AttributeSet;
import android.view.MotionEvent;
+import com.rey.material.app.ThemeManager;
import com.rey.material.drawable.RippleDrawable;
+import com.rey.material.util.ViewUtil;
-public class CompoundButton extends android.widget.CompoundButton {
+public class CompoundButton extends android.widget.CompoundButton implements ThemeManager.OnThemeChangedListener {
private RippleManager mRippleManager;
protected Drawable mButtonDrawable;
+ protected int mStyleId;
+ protected int mCurrentStyle = ThemeManager.THEME_UNDEFINED;
+
public CompoundButton(Context context) {
super(context);
@@ -53,17 +58,47 @@ private void init(Context context, AttributeSet attrs, int defStyleAttr, int def
}
setClickable(true);
+ ViewUtil.applyFont(this, attrs, defStyleAttr, defStyleRes);
applyStyle(context, attrs, defStyleAttr, defStyleRes);
+
+ mStyleId = ThemeManager.getStyleId(context, attrs, defStyleAttr, defStyleRes);
}
public void applyStyle(int resId){
+ ViewUtil.applyStyle(this, resId);
applyStyle(getContext(), null, 0, resId);
}
- private void applyStyle(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){
+ protected void applyStyle(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){
getRippleManager().onCreate(this, context, attrs, defStyleAttr, defStyleRes);
}
+ @Override
+ public void onThemeChanged(ThemeManager.OnThemeChangedEvent event) {
+ int style = ThemeManager.getInstance().getCurrentStyle(mStyleId);
+ if(mCurrentStyle != style){
+ mCurrentStyle = style;
+ applyStyle(mCurrentStyle);
+ }
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ if(mStyleId != 0) {
+ ThemeManager.getInstance().registerOnThemeChangedListener(this);
+ onThemeChanged(null);
+ }
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ mRippleManager.cancelRipple(this);
+ if(mStyleId != 0)
+ ThemeManager.getInstance().unregisterOnThemeChangedListener(this);
+ }
+
@Override
public void setBackgroundDrawable(Drawable drawable) {
Drawable background = getBackground();
diff --git a/lib/src/main/java/com/rey/material/widget/DatePicker.java b/lib/src/main/java/com/rey/material/widget/DatePicker.java
index dccfadda..d8af73b6 100644
--- a/lib/src/main/java/com/rey/material/widget/DatePicker.java
+++ b/lib/src/main/java/com/rey/material/widget/DatePicker.java
@@ -22,6 +22,7 @@
import android.widget.BaseAdapter;
import com.rey.material.R;
+import com.rey.material.app.ThemeManager;
import com.rey.material.drawable.BlankDrawable;
import com.rey.material.util.ThemeUtil;
import com.rey.material.util.TypefaceUtil;
@@ -37,14 +38,17 @@
*/
public class DatePicker extends ListView implements AbsListView.OnScrollListener{
- private Typeface mTypeface;
- private int mTextSize;
- private int mTextColor;
- private int mTextLabelColor;
- private int mTextHighlightColor;
+ protected int mStyleId;
+ protected int mCurrentStyle = ThemeManager.THEME_UNDEFINED;
+
+ private Typeface mTypeface = Typeface.DEFAULT;
+ private int mTextSize = -1;
+ private int mTextColor = 0xFF000000;
+ private int mTextLabelColor = 0xFF767676;
+ private int mTextHighlightColor = 0xFFFFFFFF;
private int mTextDisableColor;
private int mSelectionColor;
- private int mAnimDuration;
+ private int mAnimDuration = -1;
private Interpolator mInInterpolator;
private Interpolator mOutInterpolator;
@@ -146,6 +150,8 @@ private void init(Context context, AttributeSet attrs, int defStyleAttr, int def
mDayPadding = ThemeUtil.dpToPx(context, 4);
+ mSelectionColor = ThemeUtil.colorPrimary(context, 0xFF000000);
+
mCalendar = Calendar.getInstance();
mFirstDayOfWeek = mCalendar.getFirstDayOfWeek();
@@ -163,41 +169,105 @@ private void init(Context context, AttributeSet attrs, int defStyleAttr, int def
applyStyle(context, attrs, defStyleAttr, defStyleRes);
}
- public void applyStyle(int resId){
- applyStyle(getContext(), null, 0, resId);
- }
+ @Override
+ protected void applyStyle(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){
+ super.applyStyle(context, attrs, defStyleAttr, defStyleRes);
- private void applyStyle(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DatePicker, defStyleAttr, defStyleRes);
- mTextSize = a.getDimensionPixelSize(R.styleable.DatePicker_dp_dayTextSize, context.getResources().getDimensionPixelOffset(R.dimen.abc_text_size_caption_material));
- mTextColor = a.getColor(R.styleable.DatePicker_dp_textColor, 0xFF000000);
- mTextHighlightColor = a.getColor(R.styleable.DatePicker_dp_textHighlightColor, 0xFFFFFFFF);
- mTextLabelColor = a.getColor(R.styleable.DatePicker_dp_textLabelColor, 0xFF767676);
- mTextDisableColor = a.getColor(R.styleable.DatePicker_dp_textDisableColor, 0xFF767676);
- mSelectionColor = a.getColor(R.styleable.DatePicker_dp_selectionColor, ThemeUtil.colorPrimary(context, 0xFF000000));
- mAnimDuration = a.getInteger(R.styleable.DatePicker_dp_animDuration, context.getResources().getInteger(android.R.integer.config_mediumAnimTime));
- int resId = a.getResourceId(R.styleable.DatePicker_dp_inInterpolator, 0);
- if(resId != 0)
- mInInterpolator = AnimationUtils.loadInterpolator(context, resId);
- else
+
+ String familyName = null;
+ int style = -1;
+
+ int padding = -1;
+ int paddingLeft = -1;
+ int paddingRight = -1;
+ int paddingTop = -1;
+ int paddingBottom = -1;
+ boolean paddingDefined = false;
+
+ for(int i = 0, count = a.getIndexCount(); i < count; i++){
+ int attr = a.getIndex(i);
+
+ if(attr == R.styleable.DatePicker_dp_dayTextSize)
+ mTextSize = a.getDimensionPixelSize(attr, 0);
+ else if(attr == R.styleable.DatePicker_dp_textColor)
+ mTextColor = a.getColor(attr, 0);
+ else if(attr == R.styleable.DatePicker_dp_textHighlightColor)
+ mTextHighlightColor = a.getColor(attr, 0);
+ else if(attr == R.styleable.DatePicker_dp_textLabelColor)
+ mTextLabelColor = a.getColor(attr, 0);
+ else if(attr == R.styleable.DatePicker_dp_textDisableColor)
+ mTextDisableColor = a.getColor(attr, 0);
+ else if(attr == R.styleable.DatePicker_dp_selectionColor)
+ mSelectionColor = a.getColor(attr, 0);
+ else if(attr == R.styleable.DatePicker_dp_animDuration)
+ mAnimDuration = a.getInteger(attr, 0);
+ else if(attr == R.styleable.DatePicker_dp_inInterpolator)
+ mInInterpolator = AnimationUtils.loadInterpolator(context, a.getResourceId(attr, 0));
+ else if(attr == R.styleable.DatePicker_dp_outInterpolator)
+ mOutInterpolator = AnimationUtils.loadInterpolator(context, a.getResourceId(attr, 0));
+ else if(attr == R.styleable.DatePicker_dp_fontFamily)
+ familyName = a.getString(attr);
+ else if(attr == R.styleable.DatePicker_dp_textStyle)
+ style = a.getInteger(attr, 0);
+ else if(attr == R.styleable.DatePicker_android_padding) {
+ padding = a.getDimensionPixelSize(attr, 0);
+ paddingDefined = true;
+ }
+ else if(attr == R.styleable.DatePicker_android_paddingLeft) {
+ paddingLeft = a.getDimensionPixelSize(attr, 0);
+ paddingDefined = true;
+ }
+ else if(attr == R.styleable.DatePicker_android_paddingTop) {
+ paddingTop = a.getDimensionPixelSize(attr, 0);
+ paddingDefined = true;
+ }
+ else if(attr == R.styleable.DatePicker_android_paddingRight) {
+ paddingRight = a.getDimensionPixelSize(attr, 0);
+ paddingDefined = true;
+ }
+ else if(attr == R.styleable.DatePicker_android_paddingBottom) {
+ paddingBottom = a.getDimensionPixelSize(attr, 0);
+ paddingDefined = true;
+ }
+ }
+
+ if(mTextSize < 0)
+ mTextSize = context.getResources().getDimensionPixelOffset(R.dimen.abc_text_size_caption_material);
+
+ if(mAnimDuration < 0)
+ mAnimDuration = context.getResources().getInteger(android.R.integer.config_mediumAnimTime);
+
+ if(mInInterpolator == null)
mInInterpolator = new DecelerateInterpolator();
- resId = a.getResourceId(R.styleable.DatePicker_dp_outInterpolator, 0);
- if(resId != 0)
- mOutInterpolator = AnimationUtils.loadInterpolator(context, resId);
- else
+
+ if(mOutInterpolator == null)
mOutInterpolator = new DecelerateInterpolator();
- String familyName = a.getString(R.styleable.DatePicker_dp_fontFamily);
- int style = a.getInteger(R.styleable.DatePicker_dp_textStyle, Typeface.NORMAL);
- mTypeface = TypefaceUtil.load(context, familyName, style);
- int padding = a.getDimensionPixelSize(R.styleable.DatePicker_android_padding, -1);
- if(padding >= 0)
- setContentPadding(padding, padding, padding, padding);
- mPaddingLeft = a.getDimensionPixelSize(R.styleable.DatePicker_android_paddingLeft, mPaddingLeft);
- mPaddingTop = a.getDimensionPixelSize(R.styleable.DatePicker_android_paddingTop, mPaddingTop);
- mPaddingRight = a.getDimensionPixelSize(R.styleable.DatePicker_android_paddingRight, mPaddingRight);
- mPaddingBottom = a.getDimensionPixelSize(R.styleable.DatePicker_android_paddingBottom, mPaddingBottom);
+
+ if(familyName != null || style >= 0)
+ mTypeface = TypefaceUtil.load(context, familyName, style);
a.recycle();
+
+ if(paddingDefined){
+ if(padding >= 0)
+ setContentPadding(padding, padding, padding, padding);
+
+ if(paddingLeft >= 0)
+ mPaddingLeft = paddingLeft;
+
+ if(paddingTop >= 0)
+ mPaddingTop = paddingTop;
+
+ if(paddingRight >= 0)
+ mPaddingRight = paddingRight;
+
+ if(paddingBottom >= 0)
+ mPaddingBottom = paddingBottom;
+ }
+
+ requestLayout();
+ mAdapter.notifyDataSetInvalidated();
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
diff --git a/lib/src/main/java/com/rey/material/widget/EditText.java b/lib/src/main/java/com/rey/material/widget/EditText.java
index a3f1def1..8b7167ae 100644
--- a/lib/src/main/java/com/rey/material/widget/EditText.java
+++ b/lib/src/main/java/com/rey/material/widget/EditText.java
@@ -34,10 +34,10 @@
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.ActionMode;
+import android.view.Gravity;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
-import android.view.accessibility.AccessibilityEvent;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.inputmethod.CompletionInfo;
@@ -49,16 +49,20 @@
import android.widget.*;
import com.rey.material.R;
+import com.rey.material.app.ThemeManager;
import com.rey.material.drawable.DividerDrawable;
import com.rey.material.util.ThemeUtil;
import com.rey.material.util.ViewUtil;
-public class EditText extends FrameLayout {
+public class EditText extends FrameLayout implements ThemeManager.OnThemeChangedListener{
- private boolean mLabelEnable;
- private boolean mLabelVisible;
- private int mSupportMode;
- private int mAutoCompleteMode;
+ protected int mStyleId;
+ protected int mCurrentStyle = ThemeManager.THEME_UNDEFINED;
+
+ private boolean mLabelEnable = false;
+ private boolean mLabelVisible = false;
+ protected int mSupportMode = SUPPORT_MODE_NONE;
+ protected int mAutoCompleteMode = AUTOCOMPLETE_MODE_NONE;
/**
* Indicate this EditText should not show a support text.
@@ -83,7 +87,8 @@ public class EditText extends FrameLayout {
private ColorStateList mDividerColors;
private ColorStateList mDividerErrorColors;
- private boolean mDividerCompoundPadding;
+ private boolean mDividerCompoundPadding = true;
+ private int mDividerPadding = -1;
private ColorStateList mSupportColors;
private ColorStateList mSupportErrorColors;
@@ -91,8 +96,8 @@ public class EditText extends FrameLayout {
private CharSequence mSupportHelper;
private CharSequence mSupportError;
- private int mLabelInAnimId;
- private int mLabelOutAnimId;
+ private int mLabelInAnimId = 0;
+ private int mLabelOutAnimId = 0;
protected LabelView mLabelView;
protected android.widget.EditText mInputView;
@@ -129,185 +134,350 @@ public EditText(Context context, AttributeSet attrs, int defStyleAttr, int defSt
@SuppressWarnings("deprecation")
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
- private void init(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){
+ protected void init(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){
applyStyle(context, attrs, defStyleAttr, defStyleRes);
+ mStyleId = ThemeManager.getStyleId(context, attrs, defStyleAttr, defStyleRes);
}
public void applyStyle(int resId){
+ ViewUtil.applyStyle(this, resId);
applyStyle(getContext(), null, 0, resId);
}
- private void applyStyle(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){
- CharSequence text = mInputView == null ? null : mInputView.getText();
- CharSequence supportHelper = getHelper();
- CharSequence supportError = getError();
- removeAllViews();
+ private LabelView getLabelView(){
+ if(mLabelView == null){
+ mLabelView = new LabelView(getContext());
+ if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1)
+ mLabelView.setTextDirection(mIsRtl ? TEXT_DIRECTION_RTL : TEXT_DIRECTION_LTR);
+ mLabelView.setGravity(Gravity.START);
+ mLabelView.setSingleLine(true);
+ }
- TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.EditText, defStyleAttr, defStyleRes);
+ return mLabelView;
+ }
- mLabelEnable = a.getBoolean(R.styleable.EditText_et_labelEnable, false);
- mSupportMode = a.getInteger(R.styleable.EditText_et_supportMode, SUPPORT_MODE_NONE);
- mAutoCompleteMode = a.getInteger(R.styleable.EditText_et_autoCompleteMode, AUTOCOMPLETE_MODE_NONE);
+ private LabelView getSupportView(){
+ if(mSupportView == null)
+ mSupportView = new LabelView(getContext());
- switch (mAutoCompleteMode){
+ return mSupportView;
+ }
+
+ private boolean needCreateInputView(int autoCompleteMode){
+ if(mInputView == null)
+ return true;
+
+ switch (autoCompleteMode){
+ case AUTOCOMPLETE_MODE_NONE:
+ return !(mInputView instanceof InternalEditText);
case AUTOCOMPLETE_MODE_SINGLE:
- mInputView = new InternalAutoCompleteTextView(context, attrs, defStyleAttr);
- break;
+ return !(mInputView instanceof InternalAutoCompleteTextView);
case AUTOCOMPLETE_MODE_MULTI:
- mInputView = new InternalMultiAutoCompleteTextView(context, attrs, defStyleAttr);
- break;
+ return !(mInputView instanceof InternalMultiAutoCompleteTextView);
default:
- mInputView = new InternalEditText(context, attrs, defStyleAttr);
- break;
+ return false;
}
+ }
- int inputId = a.getResourceId(R.styleable.EditText_et_inputId, 0);
- mInputView.setId(inputId != 0 ? inputId : ViewUtil.generateViewId());
- mInputView.setVisibility(View.VISIBLE);
- mInputView.setFocusableInTouchMode(true);
- mDividerColors = a.getColorStateList(R.styleable.EditText_et_dividerColor);
- mDividerErrorColors = a.getColorStateList(R.styleable.EditText_et_dividerErrorColor);
- if(mDividerColors == null){
- int[][] states = new int[][]{
- new int[]{-android.R.attr.state_focused},
- new int[]{android.R.attr.state_focused, android.R.attr.state_enabled},
- };
- int[] colors = new int[]{
- ThemeUtil.colorControlNormal(context, 0xFF000000),
- ThemeUtil.colorControlActivated(context, 0xFF000000),
- };
-
- mDividerColors = new ColorStateList(states, colors);
- }
-
- if(mDividerErrorColors == null)
- mDividerErrorColors = ColorStateList.valueOf(ThemeUtil.colorAccent(context, 0xFFFF0000));
-
- int dividerHeight = a.getDimensionPixelOffset(R.styleable.EditText_et_dividerHeight, 0);
- int dividerPadding = a.getDimensionPixelOffset(R.styleable.EditText_et_dividerPadding, 0);
- int dividerAnimDuration = a.getInteger(R.styleable.EditText_et_dividerAnimDuration, context.getResources().getInteger(android.R.integer.config_shortAnimTime));
- mDividerCompoundPadding = a.getBoolean(R.styleable.EditText_et_dividerCompoundPadding, true);
- mInputView.setPadding(0, 0, 0, dividerPadding + dividerHeight);
-
- mDivider = new DividerDrawable(dividerHeight, mDividerCompoundPadding ? mInputView.getTotalPaddingLeft() : 0, mDividerCompoundPadding ? mInputView.getTotalPaddingRight() : 0, mDividerColors, dividerAnimDuration);
- mDivider.setInEditMode(isInEditMode());
- mDivider.setAnimEnable(false);
- ViewUtil.setBackground(mInputView, mDivider);
- mDivider.setAnimEnable(true);
-
- if(text != null)
- mInputView.setText(text);
- mInputView.addTextChangedListener(new InputTextWatcher());
- addView(mInputView, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
+ protected void applyStyle(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){
+ CharSequence text = mInputView == null ? null : mInputView.getText();
+ removeAllViews();
- if(mLabelEnable){
- mLabelVisible = true;
- mLabelView = new LabelView(context);
- if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1)
- mLabelView.setTextDirection(mIsRtl ? TEXT_DIRECTION_RTL : TEXT_DIRECTION_LTR);
- mLabelView.setGravity(GravityCompat.START);
- mLabelView.setSingleLine(true);
- int labelPadding = a.getDimensionPixelOffset(R.styleable.EditText_et_labelPadding, 0);
- int labelTextSize = a.getDimensionPixelSize(R.styleable.EditText_et_labelTextSize, 0);
- ColorStateList labelTextColor = a.getColorStateList(R.styleable.EditText_et_labelTextColor);
- int labelTextAppearance = a.getResourceId(R.styleable.EditText_et_labelTextAppearance, 0);
- int labelEllipsize = a.getInteger(R.styleable.EditText_et_labelEllipsize, 0);
- mLabelInAnimId = a.getResourceId(R.styleable.EditText_et_labelInAnim, 0);
- mLabelOutAnimId = a.getResourceId(R.styleable.EditText_et_labelOutAnim, 0);
-
- mLabelView.setPadding(mDivider.getPaddingLeft(), 0, mDivider.getPaddingRight(), labelPadding);
- if(labelTextAppearance > 0)
- mLabelView.setTextAppearance(context, labelTextAppearance);
- if(labelTextSize > 0)
- mLabelView.setTextSize(TypedValue.COMPLEX_UNIT_PX, labelTextSize);
- if(labelTextColor != null)
- mLabelView.setTextColor(labelTextColor);
-
- switch (labelEllipsize) {
- case 1:
- mLabelView.setEllipsize(TruncateAt.START);
- break;
- case 2:
- mLabelView.setEllipsize(TruncateAt.MIDDLE);
- break;
- case 3:
- mLabelView.setEllipsize(TruncateAt.END);
+ TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.EditText, defStyleAttr, defStyleRes);
+
+ int labelPadding = -1;
+ int labelTextSize = -1;
+ ColorStateList labelTextColor = null;
+ int supportPadding = -1;
+ int supportTextSize = -1;
+ ColorStateList supportColors = null;
+ ColorStateList supportErrorColors = null;
+ String supportHelper = null;
+ String supportError = null;
+ ColorStateList dividerColors = null;
+ ColorStateList dividerErrorColors = null;
+ int dividerHeight = -1;
+ int dividerPadding = -1;
+ int dividerAnimDuration = -1;
+
+ mAutoCompleteMode = a.getInteger(R.styleable.EditText_et_autoCompleteMode, mAutoCompleteMode);
+ if(needCreateInputView(mAutoCompleteMode)){
+ switch (mAutoCompleteMode){
+ case AUTOCOMPLETE_MODE_SINGLE:
+ mInputView = new InternalAutoCompleteTextView(context, attrs, defStyleAttr);
break;
- case 4:
- mLabelView.setEllipsize(TruncateAt.MARQUEE);
+ case AUTOCOMPLETE_MODE_MULTI:
+ mInputView = new InternalMultiAutoCompleteTextView(context, attrs, defStyleAttr);
break;
default:
- mLabelView.setEllipsize(TruncateAt.END);
+ mInputView = new InternalEditText(context, attrs, defStyleAttr);
break;
}
- addView(mLabelView, 0, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
+ ViewUtil.applyFont(mInputView, attrs, defStyleAttr, defStyleRes);
+ if(text != null)
+ mInputView.setText(text);
+
+ mInputView.addTextChangedListener(new InputTextWatcher());
+
+ if(mDivider != null){
+ mDivider.setAnimEnable(false);
+ ViewUtil.setBackground(mInputView, mDivider);
+ mDivider.setAnimEnable(true);
+ }
}
+ else
+ ViewUtil.applyStyle(mInputView, attrs, defStyleAttr, defStyleRes);
+ mInputView.setVisibility(View.VISIBLE);
+ mInputView.setFocusableInTouchMode(true);
- if(mSupportMode != SUPPORT_MODE_NONE){
- mSupportView = new LabelView(context);
- int supportPadding = a.getDimensionPixelOffset(R.styleable.EditText_et_supportPadding, 0);
- int supportTextSize = a.getDimensionPixelSize(R.styleable.EditText_et_supportTextSize, 0);
- mSupportColors = a.getColorStateList(R.styleable.EditText_et_supportTextColor);
- mSupportErrorColors = a.getColorStateList(R.styleable.EditText_et_supportTextErrorColor);
- int supportTextAppearance = a.getResourceId(R.styleable.EditText_et_supportTextAppearance, 0);
- int supportEllipsize = a.getInteger(R.styleable.EditText_et_supportEllipsize, 0);
- int supportMaxLines = a.getInteger(R.styleable.EditText_et_supportMaxLines, 0);
- int supportLines = a.getInteger(R.styleable.EditText_et_supportLines, 0);
- boolean supportSingleLine = a.getBoolean(R.styleable.EditText_et_supportSingleLine, false);
-
- mSupportView.setPadding(mDivider.getPaddingLeft(), supportPadding, mDivider.getPaddingRight(), 0);
- mSupportView.setTextSize(TypedValue.COMPLEX_UNIT_PX, supportTextSize);
- mSupportView.setTextColor(mSupportColors);
- if(supportTextAppearance > 0)
- mSupportView.setTextAppearance(context, supportTextAppearance);
- mSupportView.setSingleLine(supportSingleLine);
- if(supportMaxLines > 0)
- mSupportView.setMaxLines(supportMaxLines);
- if(supportLines > 0)
- mSupportView.setLines(supportLines);
-
- switch (supportEllipsize) {
- case 1:
- mSupportView.setEllipsize(TruncateAt.START);
- break;
- case 2:
- mSupportView.setEllipsize(TruncateAt.MIDDLE);
- break;
- case 3:
- mSupportView.setEllipsize(TruncateAt.END);
- break;
- case 4:
- mSupportView.setEllipsize(TruncateAt.MARQUEE);
- break;
- default:
- mSupportView.setEllipsize(TruncateAt.END);
- break;
+ for(int i = 0, count = a.getIndexCount(); i < count; i++){
+ int attr = a.getIndex(i);
+
+ if(attr == R.styleable.EditText_et_labelEnable)
+ mLabelEnable = a.getBoolean(attr, false);
+ else if(attr == R.styleable.EditText_et_labelPadding)
+ labelPadding = a.getDimensionPixelSize(attr, 0);
+ else if(attr == R.styleable.EditText_et_labelTextSize)
+ labelTextSize = a.getDimensionPixelSize(attr, 0);
+ else if(attr == R.styleable.EditText_et_labelTextColor)
+ labelTextColor = a.getColorStateList(attr);
+ else if(attr == R.styleable.EditText_et_labelTextAppearance)
+ getLabelView().setTextAppearance(context, a.getResourceId(attr, 0));
+ else if(attr == R.styleable.EditText_et_labelEllipsize){
+ int labelEllipsize = a.getInteger(attr, 0);
+ switch (labelEllipsize) {
+ case 1:
+ getLabelView().setEllipsize(TruncateAt.START);
+ break;
+ case 2:
+ getLabelView().setEllipsize(TruncateAt.MIDDLE);
+ break;
+ case 3:
+ getLabelView().setEllipsize(TruncateAt.END);
+ break;
+ case 4:
+ getLabelView().setEllipsize(TruncateAt.MARQUEE);
+ break;
+ default:
+ getLabelView().setEllipsize(TruncateAt.END);
+ break;
+ }
}
+ else if(attr == R.styleable.EditText_et_labelInAnim)
+ mLabelInAnimId = a.getResourceId(attr, 0);
+ else if(attr == R.styleable.EditText_et_labelOutAnim)
+ mLabelOutAnimId = a.getResourceId(attr, 0);
+ else if(attr == R.styleable.EditText_et_supportMode)
+ mSupportMode = a.getInteger(attr, 0);
+ else if(attr == R.styleable.EditText_et_supportPadding)
+ supportPadding = a.getDimensionPixelSize(attr, 0);
+ else if(attr == R.styleable.EditText_et_supportTextSize)
+ supportTextSize = a.getDimensionPixelSize(attr, 0);
+ else if(attr == R.styleable.EditText_et_supportTextColor)
+ supportColors = a.getColorStateList(attr);
+ else if(attr == R.styleable.EditText_et_supportTextErrorColor)
+ supportErrorColors = a.getColorStateList(attr);
+ else if(attr == R.styleable.EditText_et_supportTextAppearance)
+ getSupportView().setTextAppearance(context, a.getResourceId(attr, 0));
+ else if(attr == R.styleable.EditText_et_supportEllipsize){
+ int supportEllipsize = a.getInteger(attr, 0);
+ switch (supportEllipsize) {
+ case 1:
+ getSupportView().setEllipsize(TruncateAt.START);
+ break;
+ case 2:
+ getSupportView().setEllipsize(TruncateAt.MIDDLE);
+ break;
+ case 3:
+ getSupportView().setEllipsize(TruncateAt.END);
+ break;
+ case 4:
+ getSupportView().setEllipsize(TruncateAt.MARQUEE);
+ break;
+ default:
+ getSupportView().setEllipsize(TruncateAt.END);
+ break;
+ }
+ }
+ else if(attr == R.styleable.EditText_et_supportMaxLines)
+ getSupportView().setMaxLines(a.getInteger(attr, 0));
+ else if(attr == R.styleable.EditText_et_supportLines)
+ getSupportView().setLines(a.getInteger(attr, 0));
+ else if(attr == R.styleable.EditText_et_supportSingleLine)
+ getSupportView().setSingleLine(a.getBoolean(attr, false));
+ else if(attr == R.styleable.EditText_et_supportMaxChars)
+ mSupportMaxChars = a.getInteger(attr, 0);
+ else if(attr == R.styleable.EditText_et_helper)
+ supportHelper = a.getString(attr);
+ else if(attr == R.styleable.EditText_et_error)
+ supportError = a.getString(attr);
+ else if(attr == R.styleable.EditText_et_inputId)
+ mInputView.setId(a.getResourceId(attr, 0));
+ else if(attr == R.styleable.EditText_et_dividerColor)
+ dividerColors = a.getColorStateList(attr);
+ else if(attr == R.styleable.EditText_et_dividerErrorColor)
+ dividerErrorColors = a.getColorStateList(attr);
+ else if(attr == R.styleable.EditText_et_dividerHeight)
+ dividerHeight = a.getDimensionPixelSize(attr, 0);
+ else if(attr == R.styleable.EditText_et_dividerPadding)
+ dividerPadding = a.getDimensionPixelSize(attr, 0);
+ else if(attr == R.styleable.EditText_et_dividerAnimDuration)
+ dividerAnimDuration = a.getInteger(attr, 0);
+ else if(attr == R.styleable.EditText_et_dividerCompoundPadding)
+ mDividerCompoundPadding = a.getBoolean(attr, true);
+ }
+
+ a.recycle();
+
+ if(mInputView.getId() == 0)
+ mInputView.setId(ViewUtil.generateViewId());
+
+ if(mDivider == null){
+ mDividerColors = dividerColors;
+ mDividerErrorColors = dividerErrorColors;
+ if(mDividerColors == null){
+ int[][] states = new int[][]{
+ new int[]{-android.R.attr.state_focused},
+ new int[]{android.R.attr.state_focused, android.R.attr.state_enabled},
+ };
+ int[] colors = new int[]{
+ ThemeUtil.colorControlNormal(context, 0xFF000000),
+ ThemeUtil.colorControlActivated(context, 0xFF000000),
+ };
+
+ mDividerColors = new ColorStateList(states, colors);
+ }
+
+ if(mDividerErrorColors == null)
+ mDividerErrorColors = ColorStateList.valueOf(ThemeUtil.colorAccent(context, 0xFFFF0000));
+
+ if(dividerHeight < 0)
+ dividerHeight = 0;
+
+ if(dividerPadding < 0)
+ dividerPadding = 0;
+
+ if(dividerAnimDuration < 0)
+ dividerAnimDuration = context.getResources().getInteger(android.R.integer.config_shortAnimTime);
+
+ mDividerPadding = dividerPadding;
+ mInputView.setPadding(0, 0, 0, mDividerPadding + dividerHeight);
+
+ mDivider = new DividerDrawable(dividerHeight, mDividerCompoundPadding ? mInputView.getTotalPaddingLeft() : 0, mDividerCompoundPadding ? mInputView.getTotalPaddingRight() : 0, mDividerColors, dividerAnimDuration);
+ mDivider.setInEditMode(isInEditMode());
+ mDivider.setAnimEnable(false);
+ ViewUtil.setBackground(mInputView, mDivider);
+ mDivider.setAnimEnable(true);
+ }
+ else{
+ if(dividerHeight >= 0 || dividerPadding >= 0) {
+ if (dividerHeight < 0)
+ dividerHeight = mDivider.getDividerHeight();
+
+ if (dividerPadding >= 0)
+ mDividerPadding = dividerPadding;
+
+ mInputView.setPadding(0, 0, 0, mDividerPadding + dividerHeight);
+ mDivider.setDividerHeight(dividerHeight);
+ mDivider.setPadding(mDividerCompoundPadding ? mInputView.getTotalPaddingLeft() : 0, mDividerCompoundPadding ? mInputView.getTotalPaddingRight() : 0);
+ }
+
+ if(dividerColors != null)
+ mDividerColors = dividerColors;
+
+ if(dividerErrorColors != null)
+ mDividerErrorColors = dividerErrorColors;
+
+ mDivider.setColor(getError() == null ? mDividerColors : mDividerErrorColors);
+
+ if(dividerAnimDuration >= 0)
+ mDivider.setAnimationDuration(dividerAnimDuration);
+ }
+
+ if(labelPadding >= 0)
+ getLabelView().setPadding(mDivider.getPaddingLeft(), 0, mDivider.getPaddingRight(), labelPadding);
+
+ if(labelTextSize >= 0)
+ getLabelView().setTextSize(TypedValue.COMPLEX_UNIT_PX, labelTextSize);
+
+ if(labelTextColor != null)
+ getLabelView().setTextColor(labelTextColor);
+
+ if(mLabelEnable){
+ mLabelVisible = true;
+ mLabelView.setText(mInputView.getHint());
+ addView(mLabelView, 0, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
+ setLabelVisible(!TextUtils.isEmpty(mInputView.getText().toString()), false);
+ }
+
+ if(supportTextSize >= 0)
+ getSupportView().setTextSize(TypedValue.COMPLEX_UNIT_PX, supportTextSize);
+
+ if(supportColors != null)
+ mSupportColors = supportColors;
+ else if(mSupportColors == null)
+ mSupportColors = context.getResources().getColorStateList(R.color.abc_secondary_text_material_light);
+
+ if(supportErrorColors != null)
+ mSupportErrorColors = supportErrorColors;
+ else if(mSupportErrorColors == null)
+ mSupportErrorColors = ColorStateList.valueOf(ThemeUtil.colorAccent(context, 0xFFFF0000));
+
+ if(supportPadding >= 0)
+ getSupportView().setPadding(mDivider.getPaddingLeft(), supportPadding, mDivider.getPaddingRight(), 0);
+
+ if(supportHelper == null && supportError == null)
+ getSupportView().setTextColor(getError() == null ? mSupportColors : mSupportErrorColors);
+ else if(supportHelper != null)
+ setHelper(supportHelper);
+ else
+ setError(supportError);
+
+ if(mSupportMode != SUPPORT_MODE_NONE){
switch (mSupportMode) {
case SUPPORT_MODE_CHAR_COUNTER:
- mSupportMaxChars = a.getInteger(R.styleable.EditText_et_supportMaxChars, 0);
- mSupportView.setGravity(GravityCompat.END);
+ getSupportView().setGravity(Gravity.END);
updateCharCounter(mInputView.getText().length());
break;
case SUPPORT_MODE_HELPER:
case SUPPORT_MODE_HELPER_WITH_ERROR:
- mSupportView.setGravity(GravityCompat.START);
- mSupportHelper = ThemeUtil.getString(a, R.styleable.EditText_et_helper, supportHelper);
- setError(ThemeUtil.getString(a, R.styleable.EditText_et_error, supportError));
+ getSupportView().setGravity(GravityCompat.START);
break;
}
addView(mSupportView, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
}
- a.recycle();
+ addView(mInputView, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
- if(mLabelEnable){
- mLabelView.setText(mInputView.getHint());
- setLabelVisible(!TextUtils.isEmpty(mInputView.getText().toString()), false);
+ requestLayout();
+ }
+
+ @Override
+ public void onThemeChanged(ThemeManager.OnThemeChangedEvent event) {
+ int style = ThemeManager.getInstance().getCurrentStyle(mStyleId);
+ if(mCurrentStyle != style){
+ mCurrentStyle = style;
+ applyStyle(mCurrentStyle);
+ }
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ if(mStyleId != 0) {
+ ThemeManager.getInstance().registerOnThemeChangedListener(this);
+ onThemeChanged(null);
}
}
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ if(mStyleId != 0)
+ ThemeManager.getInstance().unregisterOnThemeChangedListener(this);
+ }
+
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
@Override
public void onRtlPropertiesChanged(int layoutDirection) {
@@ -446,14 +616,14 @@ public void setError(CharSequence error){
return;
if(mSupportError != null){
- mSupportView.setTextColor(mSupportErrorColors);
- mDivider.setColor(mDividerErrorColors);
- mSupportView.setText(mSupportMode == SUPPORT_MODE_HELPER ? mSupportError : TextUtils.concat(mSupportHelper, ", ", mSupportError));
+ getSupportView().setTextColor(mSupportErrorColors);
+ mDivider.setColor(mDividerErrorColors);
+ getSupportView().setText(mSupportMode == SUPPORT_MODE_HELPER ? mSupportError : TextUtils.concat(mSupportHelper, ", ", mSupportError));
}
else{
- mSupportView.setTextColor(mSupportColors);
- mDivider.setColor(mDividerColors);
- mSupportView.setText(mSupportHelper);
+ getSupportView().setTextColor(mSupportColors);
+ mDivider.setColor(mDividerColors);
+ getSupportView().setText(mSupportHelper);
}
}
@@ -473,23 +643,23 @@ public void clearError(){
private void updateCharCounter(int count){
if(count == 0){
- mSupportView.setTextColor(mSupportColors);
+ getSupportView().setTextColor(mSupportColors);
mDivider.setColor(mDividerColors);
- mSupportView.setText(null);
+ getSupportView().setText(null);
}
else{
if(mSupportMaxChars > 0){
- mSupportView.setTextColor(count > mSupportMaxChars ? mSupportErrorColors : mSupportColors);
+ getSupportView().setTextColor(count > mSupportMaxChars ? mSupportErrorColors : mSupportColors);
mDivider.setColor(count > mSupportMaxChars ? mDividerErrorColors : mDividerColors);
- mSupportView.setText(count + " / " + mSupportMaxChars);
+ getSupportView().setText(count + " / " + mSupportMaxChars);
}
else
- mSupportView.setText(String.valueOf(count));
+ getSupportView().setText(String.valueOf(count));
}
}
private void setLabelVisible(boolean visible, boolean animation){
- if(!mLabelEnable|| mLabelVisible == visible)
+ if(!mLabelEnable || mLabelVisible == visible)
return;
mLabelVisible = visible;
@@ -1823,7 +1993,7 @@ public int getMaxHeight (){
/**
* @return the maximum number of lines displayed in this TextView, or -1 if the maximum
- * height was set in pixels instead using {@link #setMaxHeight(int) or #setHeight(int)}.
+ * height was set in pixels instead using {@link #setMaxHeight(int) or #setDividerHeight(int)}.
*
* @see #setMaxLines(int)
*
@@ -1889,7 +2059,7 @@ public int getMinHeight (){
/**
* @return the minimum number of lines displayed in this TextView, or -1 if the minimum
- * height was set in pixels instead using {@link #setMinHeight(int) or #setHeight(int)}.
+ * height was set in pixels instead using {@link #setMinHeight(int) or #setDividerHeight(int)}.
*
* @see #setMinLines(int)
*
@@ -2451,9 +2621,9 @@ protected void onSelectionChanged(int selStart, int selEnd) {
if(mInputView == null)
return;
- if(mAutoCompleteMode == AUTOCOMPLETE_MODE_NONE)
+ if(mInputView instanceof InternalEditText)
((InternalEditText)mInputView).superOnSelectionChanged(selStart, selEnd);
- else if(mAutoCompleteMode == AUTOCOMPLETE_MODE_SINGLE)
+ else if(mInputView instanceof InternalAutoCompleteTextView)
((InternalAutoCompleteTextView)mInputView).superOnSelectionChanged(selStart, selEnd);
else
((InternalMultiAutoCompleteTextView)mInputView).superOnSelectionChanged(selStart, selEnd);
diff --git a/lib/src/main/java/com/rey/material/widget/FloatingActionButton.java b/lib/src/main/java/com/rey/material/widget/FloatingActionButton.java
index ed82bbbf..a11decb0 100644
--- a/lib/src/main/java/com/rey/material/widget/FloatingActionButton.java
+++ b/lib/src/main/java/com/rey/material/widget/FloatingActionButton.java
@@ -3,15 +3,9 @@
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
+import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.Canvas;
-import android.graphics.ColorFilter;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.PixelFormat;
-import android.graphics.RadialGradient;
-import android.graphics.RectF;
-import android.graphics.Shader;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Parcel;
@@ -31,22 +25,26 @@
import android.widget.RelativeLayout;
import com.rey.material.R;
+import com.rey.material.app.ThemeManager;
import com.rey.material.drawable.LineMorphingDrawable;
+import com.rey.material.drawable.OvalShadowDrawable;
import com.rey.material.drawable.RippleDrawable;
import com.rey.material.util.ThemeUtil;
import com.rey.material.util.ViewUtil;
-public class FloatingActionButton extends View {
+public class FloatingActionButton extends View implements ThemeManager.OnThemeChangedListener {
private OvalShadowDrawable mBackground;
private Drawable mIcon;
private Drawable mPrevIcon;
- private int mAnimDuration;
+ private int mAnimDuration = -1;
private Interpolator mInterpolator;
private SwitchIconAnimator mSwitchIconAnimator;
- private int mIconSize;
+ private int mIconSize = -1;
private RippleManager mRippleManager;
+ protected int mStyleId;
+ protected int mCurrentStyle = ThemeManager.THEME_UNDEFINED;
public static FloatingActionButton make(Context context, int resId){
return new FloatingActionButton(context, null, resId);
@@ -80,32 +78,93 @@ private void init(Context context, AttributeSet attrs, int defStyleAttr, int def
setClickable(true);
mSwitchIconAnimator = new SwitchIconAnimator();
applyStyle(context, attrs, defStyleAttr, defStyleRes);
+
+ mStyleId = ThemeManager.getStyleId(context, attrs, defStyleAttr, defStyleRes);
}
public void applyStyle(int resId){
+ ViewUtil.applyStyle(this, resId);
applyStyle(getContext(), null, 0, resId);
}
- private void applyStyle(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ protected void applyStyle(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.FloatingActionButton, defStyleAttr, defStyleRes);
- int radius = a.getDimensionPixelSize(R.styleable.FloatingActionButton_fab_radius, ThemeUtil.dpToPx(context, 28));
- int elevation = a.getDimensionPixelSize(R.styleable.FloatingActionButton_fab_elevation, ThemeUtil.dpToPx(context, 4));
- int bgColor = a.getColor(R.styleable.FloatingActionButton_fab_backgroundColor, ThemeUtil.colorAccent(context, 0xFFFAFAFA));
- int iconSrc = a.getResourceId(R.styleable.FloatingActionButton_fab_iconSrc, 0);
- int iconLineMorphing = a.getResourceId(R.styleable.FloatingActionButton_fab_iconLineMorphing, 0);
- mIconSize = a.getDimensionPixelSize(R.styleable.FloatingActionButton_fab_iconSize, ThemeUtil.dpToPx(context, 24));
- mAnimDuration = a.getInteger(R.styleable.FloatingActionButton_fab_animDuration, context.getResources().getInteger(android.R.integer.config_mediumAnimTime));
- int resId = a.getResourceId(R.styleable.FloatingActionButton_fab_interpolator, 0);
- if(resId != 0)
- mInterpolator = AnimationUtils.loadInterpolator(context, resId);
- else if(mInterpolator == null)
- mInterpolator = new DecelerateInterpolator();
+ int radius = -1;
+ int elevation = -1;
+ ColorStateList bgColor = null;
+ int bgAnimDuration = -1;
+ int iconSrc = 0;
+ int iconLineMorphing = 0;
+
+ for(int i = 0, count = a.getIndexCount(); i < count; i++){
+ int attr = a.getIndex(i);
+
+ if(attr == R.styleable.FloatingActionButton_fab_radius)
+ radius = a.getDimensionPixelSize(attr, 0);
+ else if(attr == R.styleable.FloatingActionButton_fab_elevation)
+ elevation = a.getDimensionPixelSize(attr, 0);
+ else if(attr == R.styleable.FloatingActionButton_fab_backgroundColor)
+ bgColor = a.getColorStateList(attr);
+ else if(attr == R.styleable.FloatingActionButton_fab_backgroundAnimDuration)
+ bgAnimDuration = a.getInteger(attr, 0);
+ else if(attr == R.styleable.FloatingActionButton_fab_iconSrc)
+ iconSrc = a.getResourceId(attr, 0);
+ else if(attr == R.styleable.FloatingActionButton_fab_iconLineMorphing)
+ iconLineMorphing = a.getResourceId(attr, 0);
+ else if(attr == R.styleable.FloatingActionButton_fab_iconSize)
+ mIconSize = a.getDimensionPixelSize(attr, 0);
+ else if(attr == R.styleable.FloatingActionButton_fab_animDuration)
+ mAnimDuration = a.getInteger(attr, 0);
+ else if(attr == R.styleable.FloatingActionButton_fab_interpolator){
+ int resId = a.getResourceId(R.styleable.FloatingActionButton_fab_interpolator, 0);
+ if(resId != 0)
+ mInterpolator = AnimationUtils.loadInterpolator(context, resId);
+ }
+ }
a.recycle();
- mBackground = new OvalShadowDrawable(radius, bgColor, elevation, elevation);
- mBackground.setBounds(0, 0, getWidth(), getHeight());
+ if(mIconSize < 0)
+ mIconSize = ThemeUtil.dpToPx(context, 24);
+
+ if(mAnimDuration < 0)
+ mAnimDuration = context.getResources().getInteger(android.R.integer.config_mediumAnimTime);
+
+ if(mInterpolator == null)
+ mInterpolator = new DecelerateInterpolator();
+
+ if(mBackground == null){
+ if(radius < 0)
+ radius = ThemeUtil.dpToPx(context, 28);
+
+ if(elevation < 0)
+ elevation = ThemeUtil.dpToPx(context, 4);
+
+ if(bgColor == null)
+ bgColor = ColorStateList.valueOf(ThemeUtil.colorAccent(context, 0));
+
+ if(bgAnimDuration < 0)
+ bgAnimDuration = 0;
+
+ mBackground = new OvalShadowDrawable(radius, bgColor, elevation, elevation, bgAnimDuration);
+ mBackground.setInEditMode(isInEditMode());
+ mBackground.setBounds(0, 0, getWidth(), getHeight());
+ mBackground.setCallback(this);
+ }
+ else{
+ if(radius >= 0)
+ mBackground.setRadius(radius);
+
+ if(bgColor != null)
+ mBackground.setColor(bgColor);
+
+ if(elevation >= 0)
+ mBackground.setShadow(elevation, elevation);
+
+ if(bgAnimDuration >= 0)
+ mBackground.setAnimationDuration(bgAnimDuration);
+ }
if(iconLineMorphing != 0)
setIcon(new LineMorphingDrawable.Builder(context, iconLineMorphing).build(), false);
@@ -119,10 +178,35 @@ else if(iconSrc != 0)
drawable.setBackgroundDrawable(null);
drawable.setMask(RippleDrawable.Mask.TYPE_OVAL, 0, 0, 0, 0, (int)mBackground.getPaddingLeft(), (int)mBackground.getPaddingTop(), (int)mBackground.getPaddingRight(), (int)mBackground.getPaddingBottom());
}
+ }
+
+ @Override
+ public void onThemeChanged(ThemeManager.OnThemeChangedEvent event) {
+ int style = ThemeManager.getInstance().getCurrentStyle(mStyleId);
+ if(mCurrentStyle != style){
+ mCurrentStyle = style;
+ applyStyle(mCurrentStyle);
+ }
+ }
- setClickable(true);
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ if(mStyleId != 0) {
+ ThemeManager.getInstance().registerOnThemeChangedListener(this);
+ onThemeChanged(null);
+ }
}
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ mRippleManager.cancelRipple(this);
+ if(mStyleId != 0)
+ ThemeManager.getInstance().unregisterOnThemeChangedListener(this);
+ }
+
+
/**
* @return The radius of the button.
*/
@@ -180,7 +264,7 @@ public void setLineMorphingState(int state, boolean animation){
/**
* @return The background color of this button.
*/
- public int getBackgroundColor(){
+ public ColorStateList getBackgroundColor(){
return mBackground.getColor();
}
@@ -217,7 +301,12 @@ public void setIcon(Drawable icon, boolean animation){
invalidate();
}
}
-
+
+ public void setBackgroundColor(ColorStateList color){
+ mBackground.setColor(color);
+ invalidate();
+ }
+
@Override
public void setBackgroundColor(int color){
mBackground.setColor(color);
@@ -351,7 +440,18 @@ public void dismiss(){
protected boolean verifyDrawable(Drawable who) {
return super.verifyDrawable(who) || mBackground == who || mIcon == who || mPrevIcon == who;
}
-
+
+ @Override
+ protected void drawableStateChanged() {
+ super.drawableStateChanged();
+ if(mBackground != null)
+ mBackground.setState(getDrawableState());
+ if(mIcon != null)
+ mIcon.setState(getDrawableState());
+ if(mPrevIcon != null)
+ mPrevIcon.setState(getDrawableState());
+ }
+
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(mBackground.getIntrinsicWidth(), mBackground.getIntrinsicHeight());
@@ -414,223 +514,6 @@ public boolean onTouchEvent(@NonNull MotionEvent event) {
boolean result = super.onTouchEvent(event);
return getRippleManager().onTouchEvent(event) || result;
}
-
- private class OvalShadowDrawable extends Drawable{
-
- private Paint mShadowPaint;
- private Paint mGlowPaint;
- private Paint mPaint;
-
- private int mRadius;
- private float mShadowSize;
- private float mShadowOffset;
-
- private Path mShadowPath;
- private Path mGlowPath;
-
- private RectF mTempRect = new RectF();
-
- private int mColor;
-
- private boolean mNeedBuildShadow = true;
-
- private static final int COLOR_SHADOW_START = 0x4C000000;
- private static final int COLOR_SHADOW_END = 0x00000000;
-
- public OvalShadowDrawable(int radius, int color, float shadowSize, float shadowOffset){
- mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
- mPaint.setStyle(Paint.Style.FILL);
-
- setColor(color);
- setRadius(radius);
- setShadow(shadowSize, shadowOffset);
- }
-
- public boolean setRadius(int radius){
- if(mRadius != radius){
- mRadius = radius;
- mNeedBuildShadow = true;
- invalidateSelf();
-
- return true;
- }
-
- return false;
- }
-
- public boolean setShadow(float size, float offset){
- if(mShadowSize != size || mShadowOffset != offset){
- mShadowSize = size;
- mShadowOffset = offset;
- mNeedBuildShadow = true;
- invalidateSelf();
-
- return true;
- }
-
- return false;
- }
-
- public void setColor(int color){
- if(mColor != color){
- mColor = color;
- mPaint.setColor(mColor);
- invalidateSelf();
- }
- }
-
- public int getColor(){
- return mColor;
- }
-
- public int getRadius(){
- return mRadius;
- }
-
- public float getShadowSize(){
- return mShadowSize;
- }
-
- public float getShadowOffset(){
- return mShadowOffset;
- }
-
- public float getPaddingLeft(){
- return mShadowSize;
- }
-
- public float getPaddingTop(){
- return mShadowSize;
- }
-
- public float getPaddingRight(){
- return mShadowSize;
- }
-
- public float getPaddingBottom(){
- return mShadowSize + mShadowOffset;
- }
-
- public float getCenterX(){
- return mRadius + mShadowSize;
- }
-
- public float getCenterY(){
- return mRadius + mShadowSize;
- }
-
- public boolean isPointerOver(float x, float y){
- float distance = (float)Math.sqrt(Math.pow(x - getCenterX(), 2) + Math.pow(y - getCenterY(), 2));
-
- return distance < mRadius;
- }
-
- @Override
- public int getIntrinsicWidth() {
- return (int)((mRadius + mShadowSize) * 2 + 0.5f);
- }
-
- @Override
- public int getIntrinsicHeight() {
- return (int)((mRadius + mShadowSize) * 2 + mShadowOffset + 0.5f);
- }
-
- private void buildShadow(){
- if(mShadowSize <= 0)
- return;
-
- if(mShadowPaint == null){
- mShadowPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
- mShadowPaint.setStyle(Paint.Style.FILL);
- mShadowPaint.setDither(true);
- }
- float startRatio = (float)mRadius / (mRadius + mShadowSize + mShadowOffset);
- mShadowPaint.setShader(new RadialGradient(0, 0, mRadius + mShadowSize,
- new int[]{COLOR_SHADOW_START, COLOR_SHADOW_START, COLOR_SHADOW_END},
- new float[]{0f, startRatio, 1f}
- , Shader.TileMode.CLAMP));
-
- if(mShadowPath == null){
- mShadowPath = new Path();
- mShadowPath.setFillType(Path.FillType.EVEN_ODD);
- }
- else
- mShadowPath.reset();
- float radius = mRadius + mShadowSize;
- mTempRect.set(-radius, -radius, radius, radius);
- mShadowPath.addOval(mTempRect, Path.Direction.CW);
- radius = mRadius - 1;
- mTempRect.set(-radius, -radius - mShadowOffset, radius, radius - mShadowOffset);
- mShadowPath.addOval(mTempRect, Path.Direction.CW);
-
- if(mGlowPaint == null){
- mGlowPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
- mGlowPaint.setStyle(Paint.Style.FILL);
- mGlowPaint.setDither(true);
- }
- startRatio = (mRadius - mShadowSize / 2f) / (mRadius + mShadowSize / 2f);
- mGlowPaint.setShader(new RadialGradient(0, 0, mRadius + mShadowSize / 2f,
- new int[]{COLOR_SHADOW_START, COLOR_SHADOW_START, COLOR_SHADOW_END},
- new float[]{0f, startRatio, 1f}
- , Shader.TileMode.CLAMP));
-
- if(mGlowPath == null){
- mGlowPath = new Path();
- mGlowPath.setFillType(Path.FillType.EVEN_ODD);
- }
- else
- mGlowPath.reset();
-
- radius = mRadius + mShadowSize / 2f;
- mTempRect.set(-radius, -radius, radius, radius);
- mGlowPath.addOval(mTempRect, Path.Direction.CW);
- radius = mRadius - 1;
- mTempRect.set(-radius, -radius, radius, radius);
- mGlowPath.addOval(mTempRect, Path.Direction.CW);
- }
-
- @Override
- public void draw(Canvas canvas) {
- if(mNeedBuildShadow){
- buildShadow();
- mNeedBuildShadow = false;
- }
- int saveCount;
-
- if(mShadowSize > 0){
- saveCount = canvas.save();
- canvas.translate(mShadowSize + mRadius, mShadowSize + mRadius + mShadowOffset);
- canvas.drawPath(mShadowPath, mShadowPaint);
- canvas.restoreToCount(saveCount);
- }
-
- saveCount = canvas.save();
- canvas.translate(mShadowSize + mRadius, mShadowSize + mRadius);
- if(mShadowSize > 0)
- canvas.drawPath(mGlowPath, mGlowPaint);
- mTempRect.set(-mRadius, -mRadius, mRadius, mRadius);
- canvas.drawOval(mTempRect, mPaint);
- canvas.restoreToCount(saveCount);
- }
-
- @Override
- public void setAlpha(int alpha) {
- mShadowPaint.setAlpha(alpha);
- mPaint.setAlpha(alpha);
- }
-
- @Override
- public void setColorFilter(ColorFilter cf) {
- mShadowPaint.setColorFilter(cf);
- mPaint.setColorFilter(cf);
- }
-
- @Override
- public int getOpacity() {
- return PixelFormat.TRANSLUCENT;
- }
-
- }
@Override
protected Parcelable onSaveInstanceState() {
diff --git a/lib/src/main/java/com/rey/material/widget/ImageButton.java b/lib/src/main/java/com/rey/material/widget/ImageButton.java
index 248a84ad..fc4998ab 100644
--- a/lib/src/main/java/com/rey/material/widget/ImageButton.java
+++ b/lib/src/main/java/com/rey/material/widget/ImageButton.java
@@ -6,11 +6,15 @@
import android.util.AttributeSet;
import android.view.MotionEvent;
+import com.rey.material.app.ThemeManager;
import com.rey.material.drawable.RippleDrawable;
+import com.rey.material.util.ViewUtil;
-public class ImageButton extends android.widget.ImageButton {
+public class ImageButton extends android.widget.ImageButton implements ThemeManager.OnThemeChangedListener{
private RippleManager mRippleManager;
+ protected int mStyleId;
+ protected int mCurrentStyle = ThemeManager.THEME_UNDEFINED;
public ImageButton(Context context) {
super(context);
@@ -38,16 +42,45 @@ public ImageButton(Context context, AttributeSet attrs, int defStyleAttr, int de
private void init(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){
applyStyle(context, attrs, defStyleAttr, defStyleRes);
+
+ mStyleId = ThemeManager.getStyleId(context, attrs, defStyleAttr, defStyleRes);
}
public void applyStyle(int resId){
+ ViewUtil.applyStyle(this, resId);
applyStyle(getContext(), null, 0, resId);
}
- private void applyStyle(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){
+ protected void applyStyle(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){
getRippleManager().onCreate(this, context, attrs, defStyleAttr, defStyleRes);
}
+ @Override
+ public void onThemeChanged(ThemeManager.OnThemeChangedEvent event) {
+ int style = ThemeManager.getInstance().getCurrentStyle(mStyleId);
+ if(mCurrentStyle != style){
+ mCurrentStyle = style;
+ applyStyle(mCurrentStyle);
+ }
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ if(mStyleId != 0) {
+ ThemeManager.getInstance().registerOnThemeChangedListener(this);
+ onThemeChanged(null);
+ }
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ mRippleManager.cancelRipple(this);
+ if(mStyleId != 0)
+ ThemeManager.getInstance().unregisterOnThemeChangedListener(this);
+ }
+
@Override
public void setBackgroundDrawable(Drawable drawable) {
Drawable background = getBackground();
diff --git a/lib/src/main/java/com/rey/material/widget/ListPopupWindow.java b/lib/src/main/java/com/rey/material/widget/ListPopupWindow.java
index 9710965c..037ee103 100644
--- a/lib/src/main/java/com/rey/material/widget/ListPopupWindow.java
+++ b/lib/src/main/java/com/rey/material/widget/ListPopupWindow.java
@@ -75,7 +75,7 @@ public class ListPopupWindow {
static {
try {
- sClipToWindowEnabledMethod = PopupWindow.class.getDeclaredMethod(
+ sClipToWindowEnabledMethod = android.widget.PopupWindow.class.getDeclaredMethod(
"setClipToScreenEnabled", boolean.class);
} catch (NoSuchMethodException e) {
Log.i(TAG, "Could not find method setClipToScreenEnabled() on PopupWindow. Oh well.");
@@ -681,7 +681,7 @@ public boolean onPreDraw() {
}
});
- }
+ }
}
/**
@@ -1062,6 +1062,15 @@ public ListPopupWindow getPopup() {
};
}
+ private int getSystemBarHeight(String resourceName) {
+ int height = 0;
+ int resourceId = mContext.getResources().getIdentifier(resourceName, "dimen", "android");
+ if (resourceId > 0) {
+ height = mContext.getResources().getDimensionPixelSize(resourceId);
+ }
+ return height;
+ }
+
/**
* Builds the popup window's content and returns the height the popup
* should have. Returns -1 when the content already exists.
@@ -1190,13 +1199,23 @@ public void onNothingSelected(AdapterView> parent) {
}
} else {
mTempRect.setEmpty();
- }
+ }
+
+ int systemBarsReservedSpace = 0;
+ if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ // getMaxAvailableHeight() on Lollipop seems to ignore the system bars.
+ systemBarsReservedSpace = Math.max(
+ getSystemBarHeight("status_bar_height"),
+ getSystemBarHeight("navigation_bar_height")
+ );
+ }
// Max height available on the screen for a popup.
boolean ignoreBottomDecorations =
mPopup.getInputMethodMode() == PopupWindow.INPUT_METHOD_NOT_NEEDED;
final int maxHeight = mPopup.getMaxAvailableHeight(
- getAnchorView(), mDropDownVerticalOffset /*, ignoreBottomDecorations*/);
+ getAnchorView(), mDropDownVerticalOffset /*, ignoreBottomDecorations*/)
+ - systemBarsReservedSpace;
if (mDropDownAlwaysVisible || mDropDownHeight == ViewGroup.LayoutParams.MATCH_PARENT) {
return maxHeight + padding;
@@ -1796,7 +1815,9 @@ private void setPopupClipToScreenEnabled(boolean clip) {
} catch (Exception e) {
Log.i(TAG, "Could not call setClipToScreenEnabled() on PopupWindow. Oh well.");
}
+ } else if(clip && Build.VERSION.SDK_INT >= Build.VERSION_CODES.CUPCAKE) {
+ mPopup.setClippingEnabled(false);
}
}
-
+
}
\ No newline at end of file
diff --git a/lib/src/main/java/com/rey/material/widget/ListView.java b/lib/src/main/java/com/rey/material/widget/ListView.java
index 18235e6c..e2b8d3cf 100644
--- a/lib/src/main/java/com/rey/material/widget/ListView.java
+++ b/lib/src/main/java/com/rey/material/widget/ListView.java
@@ -5,9 +5,15 @@
import android.util.AttributeSet;
import android.view.View;
-public class ListView extends ListViewCompat {
+import com.rey.material.app.ThemeManager;
+import com.rey.material.util.ViewUtil;
+
+public class ListView extends ListViewCompat implements ThemeManager.OnThemeChangedListener{
private RecyclerListener mRecyclerListener;
+
+ protected int mStyleId;
+ protected int mCurrentStyle = ThemeManager.THEME_UNDEFINED;
public ListView(Context context) {
super(context);
@@ -46,8 +52,43 @@ public void onMovedToScrapHeap(View view) {
}
});
+
+ mStyleId = ThemeManager.getStyleId(context, attrs, defStyleAttr, defStyleRes);
}
-
+
+ public void applyStyle(int resId){
+ ViewUtil.applyStyle(this, resId);
+ applyStyle(getContext(), null, 0, resId);
+ }
+
+ protected void applyStyle(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){
+ }
+
+ @Override
+ public void onThemeChanged(ThemeManager.OnThemeChangedEvent event) {
+ int style = ThemeManager.getInstance().getCurrentStyle(mStyleId);
+ if(mCurrentStyle != style){
+ mCurrentStyle = style;
+ applyStyle(mCurrentStyle);
+ }
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ if(mStyleId != 0) {
+ ThemeManager.getInstance().registerOnThemeChangedListener(this);
+ onThemeChanged(null);
+ }
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ if(mStyleId != 0)
+ ThemeManager.getInstance().unregisterOnThemeChangedListener(this);
+ }
+
@Override
public void setRecyclerListener(RecyclerListener listener) {
mRecyclerListener = listener;
diff --git a/lib/src/main/java/com/rey/material/widget/ProgressView.java b/lib/src/main/java/com/rey/material/widget/ProgressView.java
index 29ef7aff..ee8f2979 100644
--- a/lib/src/main/java/com/rey/material/widget/ProgressView.java
+++ b/lib/src/main/java/com/rey/material/widget/ProgressView.java
@@ -10,14 +10,18 @@
import android.view.View;
import com.rey.material.R;
+import com.rey.material.app.ThemeManager;
import com.rey.material.drawable.CircularProgressDrawable;
import com.rey.material.drawable.LinearProgressDrawable;
import com.rey.material.util.ViewUtil;
-public class ProgressView extends View {
+public class ProgressView extends View implements ThemeManager.OnThemeChangedListener{
- private boolean mAutostart;
- private boolean mCircular;
+ protected int mStyleId;
+ protected int mCurrentStyle = ThemeManager.THEME_UNDEFINED;
+
+ private boolean mAutostart = false;
+ private boolean mCircular = true;
private int mProgressId;
public static final int MODE_DETERMINATE = 0;
@@ -50,41 +54,123 @@ public ProgressView(Context context, AttributeSet attrs, int defStyleAttr, int d
private void init(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){
applyStyle(context, attrs, defStyleAttr, defStyleRes);
+
+ mStyleId = ThemeManager.getStyleId(context, attrs, defStyleAttr, defStyleRes);
}
public void applyStyle(int resId){
+ ViewUtil.applyStyle(this, resId);
applyStyle(getContext(), null, 0, resId);
}
- private void applyStyle(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){
+ private boolean needCreateProgress(boolean circular){
+ if(mProgressDrawable == null)
+ return true;
+
+ if(circular)
+ return !(mProgressDrawable instanceof CircularProgressDrawable);
+ else
+ return !(mProgressDrawable instanceof LinearProgressDrawable);
+ }
+
+ protected void applyStyle(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ProgressView, defStyleAttr, defStyleRes);
- mAutostart = a.getBoolean(R.styleable.ProgressView_pv_autostart, true);
- mCircular = a.getBoolean(R.styleable.ProgressView_pv_circular, true);
- mProgressId = a.getResourceId(R.styleable.ProgressView_pv_progressStyle, 0);
- if(mProgressId == 0)
- mProgressId = mCircular ? R.style.Material_Drawable_CircularProgress : R.style.Material_Drawable_LinearProgress;
+ int progressId = 0;
+ int progressMode = -1;
+ float progress = -1;
+ float secondaryProgress = -1;
+
+ for(int i = 0, count = a.getIndexCount(); i < count; i++){
+ int attr = a.getIndex(i);
- if(mCircular) {
- mProgressDrawable = new CircularProgressDrawable.Builder(context, mProgressId).build();
- if(a.hasValue(R.styleable.ProgressView_pv_progressMode))
- ((CircularProgressDrawable)mProgressDrawable).setProgressMode(a.getInt(R.styleable.ProgressView_pv_progressMode, MODE_INDETERMINATE));
+ if(attr == R.styleable.ProgressView_pv_autostart)
+ mAutostart = a.getBoolean(attr, false);
+ else if(attr == R.styleable.ProgressView_pv_circular)
+ mCircular = a.getBoolean(attr, true);
+ else if(attr == R.styleable.ProgressView_pv_progressStyle)
+ progressId = a.getResourceId(attr, 0);
+ else if(attr == R.styleable.ProgressView_pv_progressMode)
+ progressMode = a.getInteger(attr, 0);
+ else if(attr == R.styleable.ProgressView_pv_progress)
+ progress = a.getFloat(attr, 0);
+ else if(attr == R.styleable.ProgressView_pv_secondaryProgress)
+ secondaryProgress = a.getFloat(attr, 0);
}
- else{
- mProgressDrawable = new LinearProgressDrawable.Builder(context, mProgressId).build();
- if(a.hasValue(R.styleable.ProgressView_pv_progressMode))
- ((LinearProgressDrawable)mProgressDrawable).setProgressMode(a.getInt(R.styleable.ProgressView_pv_progressMode, MODE_INDETERMINATE));
+
+ a.recycle();
+
+ boolean needStart = false;
+
+ if(needCreateProgress(mCircular)){
+ mProgressId = progressId;
+ if(mProgressId == 0)
+ mProgressId = mCircular ? R.style.Material_Drawable_CircularProgress : R.style.Material_Drawable_LinearProgress;
+
+ needStart = mProgressDrawable != null && ((Animatable)mProgressDrawable).isRunning();
+ mProgressDrawable = mCircular ? new CircularProgressDrawable.Builder(context, mProgressId).build() : new LinearProgressDrawable.Builder(context, mProgressId).build();
+ ViewUtil.setBackground(this, mProgressDrawable);
+ }
+ else if(mProgressId != progressId){
+ mProgressId = progressId;
+ if(mProgressDrawable instanceof CircularProgressDrawable)
+ ((CircularProgressDrawable) mProgressDrawable).applyStyle(context, mProgressId);
+ else
+ ((LinearProgressDrawable)mProgressDrawable).applyStyle(context, mProgressId);
}
- if(a.hasValue(R.styleable.ProgressView_pv_progress))
- setProgress(a.getFloat(R.styleable.ProgressView_pv_progress, 0));
+ if(progressMode >= 0) {
+ if(mProgressDrawable instanceof CircularProgressDrawable)
+ ((CircularProgressDrawable) mProgressDrawable).setProgressMode(progressMode);
+ else
+ ((LinearProgressDrawable)mProgressDrawable).setProgressMode(progressMode);
+ }
- if(a.hasValue(R.styleable.ProgressView_pv_secondaryProgress))
- setSecondaryProgress(a.getFloat(R.styleable.ProgressView_pv_secondaryProgress, 0));
+ if(progress >= 0)
+ setProgress(progress);
- a.recycle();
+ if(secondaryProgress >= 0)
+ setSecondaryProgress(secondaryProgress);
- ViewUtil.setBackground(this, mProgressDrawable);
+ if(needStart)
+ start();
+
+// mAutostart = a.getBoolean(R.styleable.ProgressView_pv_autostart, true);
+// mCircular = a.getBoolean(R.styleable.ProgressView_pv_circular, true);
+// mProgressId = a.getResourceId(R.styleable.ProgressView_pv_progressStyle, 0);
+//
+// if(mProgressId == 0)
+// mProgressId = mCircular ? R.style.Material_Drawable_CircularProgress : R.style.Material_Drawable_LinearProgress;
+//
+// if(mCircular) {
+// mProgressDrawable = new CircularProgressDrawable.Builder(context, mProgressId).build();
+// if(a.hasValue(R.styleable.ProgressView_pv_progressMode))
+// ((CircularProgressDrawable)mProgressDrawable).setProgressMode(a.getInt(R.styleable.ProgressView_pv_progressMode, MODE_INDETERMINATE));
+// }
+// else{
+// mProgressDrawable = new LinearProgressDrawable.Builder(context, mProgressId).build();
+// if(a.hasValue(R.styleable.ProgressView_pv_progressMode))
+// ((LinearProgressDrawable)mProgressDrawable).setProgressMode(a.getInt(R.styleable.ProgressView_pv_progressMode, MODE_INDETERMINATE));
+// }
+//
+// if(a.hasValue(R.styleable.ProgressView_pv_progress))
+// setProgress(a.getFloat(R.styleable.ProgressView_pv_progress, 0));
+//
+// if(a.hasValue(R.styleable.ProgressView_pv_secondaryProgress))
+// setSecondaryProgress(a.getFloat(R.styleable.ProgressView_pv_secondaryProgress, 0));
+//
+// a.recycle();
+//
+// ViewUtil.setBackground(this, mProgressDrawable);
+ }
+
+ @Override
+ public void onThemeChanged(ThemeManager.OnThemeChangedEvent event) {
+ int style = ThemeManager.getInstance().getCurrentStyle(mStyleId);
+ if(mCurrentStyle != style){
+ mCurrentStyle = style;
+ applyStyle(mCurrentStyle);
+ }
}
@Override
@@ -107,6 +193,10 @@ protected void onAttachedToWindow() {
super.onAttachedToWindow();
if(getVisibility() == View.VISIBLE && mAutostart)
start();
+ if(mStyleId != 0) {
+ ThemeManager.getInstance().registerOnThemeChangedListener(this);
+ onThemeChanged(null);
+ }
}
@Override
@@ -115,6 +205,8 @@ protected void onDetachedFromWindow() {
stop();
super.onDetachedFromWindow();
+ if(mStyleId != 0)
+ ThemeManager.getInstance().unregisterOnThemeChangedListener(this);
}
public int getProgressMode(){
diff --git a/lib/src/main/java/com/rey/material/widget/RadioButton.java b/lib/src/main/java/com/rey/material/widget/RadioButton.java
index 6ca00d28..44d453cf 100644
--- a/lib/src/main/java/com/rey/material/widget/RadioButton.java
+++ b/lib/src/main/java/com/rey/material/widget/RadioButton.java
@@ -10,37 +10,24 @@ public class RadioButton extends CompoundButton {
public RadioButton(Context context) {
super(context);
-
- init(context, null, 0, 0);
}
public RadioButton(Context context, AttributeSet attrs) {
super(context, attrs);
-
- init(context, attrs, 0, 0);
}
public RadioButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
-
- init(context, attrs, defStyleAttr, 0);
}
public RadioButton(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr);
-
- init(context, attrs, defStyleAttr, defStyleRes);
- }
-
- private void init(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){
- applyStyle(context, attrs, defStyleAttr, defStyleRes);
}
- public void applyStyle(int resId){
- applyStyle(getContext(), null, 0, resId);
- }
+ @Override
+ protected void applyStyle(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){
+ super.applyStyle(context, attrs, defStyleAttr, defStyleRes);
- private void applyStyle(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){
RadioButtonDrawable drawable = new RadioButtonDrawable.Builder(context, attrs, defStyleAttr, defStyleRes).build();
drawable.setInEditMode(isInEditMode());
drawable.setAnimEnable(false);
diff --git a/lib/src/main/java/com/rey/material/widget/RippleManager.java b/lib/src/main/java/com/rey/material/widget/RippleManager.java
index 996d4398..b46a1c7e 100644
--- a/lib/src/main/java/com/rey/material/widget/RippleManager.java
+++ b/lib/src/main/java/com/rey/material/widget/RippleManager.java
@@ -19,6 +19,7 @@ public final class RippleManager implements View.OnClickListener, Runnable{
private View.OnClickListener mClickListener;
private View mView;
+ private boolean mClickScheduled = false;
public RippleManager(){}
@@ -41,11 +42,11 @@ public void onCreate(View v, Context context, AttributeSet attrs, int defStyleAt
RippleDrawable drawable = null;
if(rippleStyle != 0)
- drawable = new RippleDrawable.Builder(context, rippleStyle).backgroundDrawable(mView.getBackground()).build();
+ drawable = new RippleDrawable.Builder(context, rippleStyle).backgroundDrawable(getBackground(mView)).build();
else{
boolean rippleEnable = a.getBoolean(R.styleable.RippleView_rd_enable, false);
if(rippleEnable)
- drawable = new RippleDrawable.Builder(context, attrs, defStyleAttr, defStyleRes).backgroundDrawable(mView.getBackground()).build();
+ drawable = new RippleDrawable.Builder(context, attrs, defStyleAttr, defStyleRes).backgroundDrawable(getBackground(mView)).build();
}
a.recycle();
@@ -53,6 +54,17 @@ public void onCreate(View v, Context context, AttributeSet attrs, int defStyleAt
if(drawable != null)
ViewUtil.setBackground(mView, drawable);
}
+
+ private Drawable getBackground(View v){
+ Drawable background = v.getBackground();
+ if(background == null)
+ return null;
+
+ if(background instanceof RippleDrawable)
+ return ((RippleDrawable)background).getBackgroundDrawable();
+
+ return background;
+ }
public void setOnClickListener(View.OnClickListener l) {
mClickListener = l;
@@ -73,14 +85,17 @@ public void onClick(View v) {
else if(background instanceof ToolbarRippleDrawable)
delay = ((ToolbarRippleDrawable)background).getClickDelayTime();
- if(delay > 0 && mView.getHandler() != null)
- mView.getHandler().postDelayed(this, delay);
+ if(delay > 0 && mView.getHandler() != null && !mClickScheduled) {
+ mClickScheduled = true;
+ mView.getHandler().postDelayed(this, delay);
+ }
else
run();
}
@Override
public void run() {
+ mClickScheduled = false;
if(mClickListener != null)
mClickListener.onClick(mView);
}
diff --git a/lib/src/main/java/com/rey/material/widget/Slider.java b/lib/src/main/java/com/rey/material/widget/Slider.java
index 4a35e46e..b34817fb 100644
--- a/lib/src/main/java/com/rey/material/widget/Slider.java
+++ b/lib/src/main/java/com/rey/material/widget/Slider.java
@@ -14,7 +14,6 @@
import android.os.Parcelable;
import android.os.SystemClock;
import android.support.annotation.NonNull;
-import android.transition.Slide;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.MotionEvent;
@@ -25,6 +24,7 @@
import android.view.animation.Interpolator;
import com.rey.material.R;
+import com.rey.material.app.ThemeManager;
import com.rey.material.drawable.RippleDrawable;
import com.rey.material.util.ColorUtil;
import com.rey.material.util.ThemeUtil;
@@ -34,9 +34,11 @@
/**
* Created by Ret on 3/18/2015.
*/
-public class Slider extends View{
+public class Slider extends View implements ThemeManager.OnThemeChangedListener{
private RippleManager mRippleManager;
+ protected int mStyleId;
+ protected int mCurrentStyle = ThemeManager.THEME_UNDEFINED;
private Paint mPaint;
private RectF mDrawRect;
@@ -53,18 +55,18 @@ public class Slider extends View{
private int mPrimaryColor;
private int mSecondaryColor;
- private int mTrackSize;
- private Paint.Cap mTrackCap;
- private int mThumbBorderSize;
- private int mThumbRadius;
- private int mThumbFocusRadius;
- private float mThumbPosition;
- private Typeface mTypeface;
- private int mTextSize;
- private int mTextColor;
+ private int mTrackSize = -1;
+ private Paint.Cap mTrackCap = Paint.Cap.BUTT;
+ private int mThumbBorderSize = -1;
+ private int mThumbRadius = -1;
+ private int mThumbFocusRadius = -1;
+ private float mThumbPosition = -1;
+ private Typeface mTypeface = Typeface.DEFAULT;
+ private int mTextSize = -1;
+ private int mTextColor = 0xFFFFFFFF;
private int mGravity = Gravity.CENTER;
- private int mTravelAnimationDuration;
- private int mTransformAnimationDuration;
+ private int mTravelAnimationDuration = -1;
+ private int mTransformAnimationDuration = -1;
private Interpolator mInterpolator;
private int mTouchSlop;
@@ -72,6 +74,7 @@ public class Slider extends View{
private boolean mIsDragging;
private float mThumbCurrentRadius;
private float mThumbFillPercent;
+ private boolean mAlwaysFillThumb = false;
private int mTextHeight;
private int mMemoValue;
private String mValueText;
@@ -128,6 +131,10 @@ public Slider(Context context, AttributeSet attrs, int defStyleAttr, int defStyl
private void init(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ //default color
+ mPrimaryColor = ThemeUtil.colorControlActivated(context, 0xFF000000);
+ mSecondaryColor = ThemeUtil.colorControlNormal(context, 0xFF000000);
+
mDrawRect = new RectF();
mTempRect = new RectF();
mLeftTrackPath = new Path();
@@ -141,55 +148,163 @@ private void init(Context context, AttributeSet attrs, int defStyleAttr, int def
mMemoPoint = new PointF();
applyStyle(context, attrs, defStyleAttr, defStyleRes);
+
+ mStyleId = ThemeManager.getStyleId(context, attrs, defStyleAttr, defStyleRes);
}
public void applyStyle(int resId){
+ ViewUtil.applyStyle(this, resId);
applyStyle(getContext(), null, 0, resId);
}
- private void applyStyle(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){
+ protected void applyStyle(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){
getRippleManager().onCreate(this, context, attrs, defStyleAttr, defStyleRes);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Slider, defStyleAttr, defStyleRes);
- mDiscreteMode = a.getBoolean(R.styleable.Slider_sl_discreteMode, mDiscreteMode);
- mPrimaryColor = a.getColor(R.styleable.Slider_sl_primaryColor, ThemeUtil.colorControlActivated(context, 0xFF000000));
- mSecondaryColor = a.getColor(R.styleable.Slider_sl_secondaryColor, ThemeUtil.colorControlNormal(context, 0xFF000000));
- mTrackSize = a.getDimensionPixelSize(R.styleable.Slider_sl_trackSize, ThemeUtil.dpToPx(context, 2));
- int cap = a.getInteger(R.styleable.Slider_sl_trackCap, 0);
- if(cap == 0)
- mTrackCap = Paint.Cap.BUTT;
- else if(cap == 1)
- mTrackCap = Paint.Cap.ROUND;
- else
- mTrackCap = Paint.Cap.SQUARE;
- mThumbBorderSize = a.getDimensionPixelSize(R.styleable.Slider_sl_thumbBorderSize, ThemeUtil.dpToPx(context, 2));
- mThumbRadius = a.getDimensionPixelSize(R.styleable.Slider_sl_thumbRadius, ThemeUtil.dpToPx(context, 10));
- mThumbFocusRadius = a.getDimensionPixelSize(R.styleable.Slider_sl_thumbFocusRadius, ThemeUtil.dpToPx(context, 14));
- mTravelAnimationDuration = a.getInteger(R.styleable.Slider_sl_travelAnimDuration, context.getResources().getInteger(android.R.integer.config_mediumAnimTime));
- mTransformAnimationDuration = a.getInteger(R.styleable.Slider_sl_travelAnimDuration, context.getResources().getInteger(android.R.integer.config_shortAnimTime));
- int resId = a.getResourceId(R.styleable.Slider_sl_interpolator, 0);
- mInterpolator = resId != 0 ? AnimationUtils.loadInterpolator(context, resId) : new DecelerateInterpolator();
- mGravity = a.getInt(R.styleable.Slider_android_gravity, Gravity.CENTER_VERTICAL);
- mMinValue = a.getInteger(R.styleable.Slider_sl_minValue, mMinValue);
- mMaxValue = a.getInteger(R.styleable.Slider_sl_maxValue, mMaxValue);
- mStepValue = a.getInteger(R.styleable.Slider_sl_stepValue, mStepValue);
- setValue(a.getInteger(R.styleable.Slider_sl_value, getValue()), false);
-
- String familyName = a.getString(R.styleable.Slider_sl_fontFamily);
- int style = a.getInteger(R.styleable.Slider_sl_textStyle, Typeface.NORMAL);
-
- mTypeface = TypefaceUtil.load(context, familyName, style);
- mTextColor = a.getColor(R.styleable.Slider_sl_textColor, 0xFFFFFFFF);
- mTextSize = a.getDimensionPixelSize(R.styleable.Slider_sl_textSize, context.getResources().getDimensionPixelOffset(R.dimen.abc_text_size_small_material));
- setEnabled(a.getBoolean(R.styleable.Slider_android_enabled, true));
+ int minValue = getMinValue();
+ int maxValue = getMaxValue();
+ boolean valueRangeDefined = false;
+ int value = -1;
+ boolean valueDefined = false;
+ String familyName = null;
+ int style = Typeface.NORMAL;
+ boolean textStyleDefined = false;
+ for(int i = 0, count = a.getIndexCount(); i < count; i++){
+ int attr = a.getIndex(i);
+ if(attr == R.styleable.Slider_sl_discreteMode)
+ mDiscreteMode = a.getBoolean(attr, false);
+ else if(attr == R.styleable.Slider_sl_primaryColor)
+ mPrimaryColor = a.getColor(attr, 0);
+ else if(attr == R.styleable.Slider_sl_secondaryColor)
+ mSecondaryColor = a.getColor(attr, 0);
+ else if(attr == R.styleable.Slider_sl_trackSize)
+ mTrackSize = a.getDimensionPixelSize(attr, 0);
+ else if(attr == R.styleable.Slider_sl_trackCap) {
+ int cap = a.getInteger(attr, 0);
+ if(cap == 0)
+ mTrackCap = Paint.Cap.BUTT;
+ else if(cap == 1)
+ mTrackCap = Paint.Cap.ROUND;
+ else
+ mTrackCap = Paint.Cap.SQUARE;
+ }
+ else if(attr == R.styleable.Slider_sl_thumbBorderSize)
+ mThumbBorderSize = a.getDimensionPixelSize(attr, 0);
+ else if(attr == R.styleable.Slider_sl_thumbRadius)
+ mThumbRadius = a.getDimensionPixelSize(attr, 0);
+ else if(attr == R.styleable.Slider_sl_thumbFocusRadius)
+ mThumbFocusRadius = a.getDimensionPixelSize(attr, 0);
+ else if(attr == R.styleable.Slider_sl_travelAnimDuration) {
+ mTravelAnimationDuration = a.getInteger(attr, 0);
+ mTransformAnimationDuration = mTravelAnimationDuration;
+ }
+ else if(attr == R.styleable.Slider_sl_alwaysFillThumb) {
+ mAlwaysFillThumb = a.getBoolean(R.styleable.Slider_sl_alwaysFillThumb, false);
+ }
+ else if(attr == R.styleable.Slider_sl_interpolator){
+ int resId = a.getResourceId(R.styleable.Slider_sl_interpolator, 0);
+ mInterpolator = AnimationUtils.loadInterpolator(context, resId);
+ }
+ else if(attr == R.styleable.Slider_android_gravity)
+ mGravity = a.getInteger(attr, 0);
+ else if(attr == R.styleable.Slider_sl_minValue) {
+ minValue = a.getInteger(attr, 0);
+ valueRangeDefined = true;
+ }
+ else if(attr == R.styleable.Slider_sl_maxValue) {
+ maxValue = a.getInteger(attr, 0);
+ valueRangeDefined = true;
+ }
+ else if(attr == R.styleable.Slider_sl_stepValue)
+ mStepValue = a.getInteger(attr, 0);
+ else if(attr == R.styleable.Slider_sl_value) {
+ value = a.getInteger(attr, 0);
+ valueDefined = true;
+ }
+ else if(attr == R.styleable.Slider_sl_fontFamily) {
+ familyName = a.getString(attr);
+ textStyleDefined = true;
+ }
+ else if(attr == R.styleable.Slider_sl_textStyle) {
+ style = a.getInteger(attr, 0);
+ textStyleDefined = true;
+ }
+ else if(attr == R.styleable.Slider_sl_textColor)
+ mTextColor = a.getColor(attr, 0);
+ else if(attr == R.styleable.Slider_sl_textSize)
+ mTextSize = a.getDimensionPixelSize(attr, 0);
+ else if(attr == R.styleable.Slider_android_enabled)
+ setEnabled(a.getBoolean(attr, true));
+ }
a.recycle();
+ if(mTrackSize < 0)
+ mTrackSize = ThemeUtil.dpToPx(context, 2);
+
+ if(mThumbBorderSize < 0)
+ mThumbBorderSize = ThemeUtil.dpToPx(context, 2);
+
+ if(mThumbRadius < 0)
+ mThumbRadius = ThemeUtil.dpToPx(context, 10);
+
+ if(mThumbFocusRadius < 0)
+ mThumbFocusRadius = ThemeUtil.dpToPx(context, 14);
+
+ if(mTravelAnimationDuration < 0){
+ mTravelAnimationDuration = context.getResources().getInteger(android.R.integer.config_mediumAnimTime);
+ mTransformAnimationDuration = mTravelAnimationDuration;
+ }
+
+ if(mInterpolator == null)
+ mInterpolator = new DecelerateInterpolator();
+
+ if(valueRangeDefined)
+ setValueRange(minValue, maxValue, false);
+
+ if(valueDefined)
+ setValue(value, false);
+ else if(mThumbPosition < 0)
+ setValue(mMinValue, false);
+
+ if(textStyleDefined)
+ mTypeface = TypefaceUtil.load(context, familyName, style);
+
+ if(mTextSize < 0)
+ mTextSize = context.getResources().getDimensionPixelOffset(R.dimen.abc_text_size_small_material);
+
mPaint.setTextSize(mTextSize);
mPaint.setTextAlign(Paint.Align.CENTER);
mPaint.setTypeface(mTypeface);
measureText();
+ invalidate();
+ }
+
+ @Override
+ public void onThemeChanged(ThemeManager.OnThemeChangedEvent event) {
+ int style = ThemeManager.getInstance().getCurrentStyle(mStyleId);
+ if(mCurrentStyle != style){
+ mCurrentStyle = style;
+ applyStyle(mCurrentStyle);
+ }
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ if(mStyleId != 0) {
+ ThemeManager.getInstance().registerOnThemeChangedListener(this);
+ onThemeChanged(null);
+ }
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ mRippleManager.cancelRipple(this);
+ if(mStyleId != 0)
+ ThemeManager.getInstance().unregisterOnThemeChangedListener(this);
}
private void measureText(){
@@ -303,7 +418,7 @@ private void setPosition(float pos, boolean moveAnimation, boolean transformAnim
}
else{
mThumbCurrentRadius = mThumbRadius;
- mThumbFillPercent = mThumbPosition == 0 ? 0 : 1;
+ mThumbFillPercent = (mAlwaysFillThumb || mThumbPosition != 0) ? 1 : 0;
invalidate();
}
}
@@ -315,6 +430,32 @@ private void setPosition(float pos, boolean moveAnimation, boolean transformAnim
mOnPositionChangeListener.onPositionChanged(this, fromUser, oldPos, newPos, oldValue, newValue);
}
+ /**
+ * Changes the primary color and invalidates the view to force a redraw.
+ * @param color New color to assign to mPrimaryColor.
+ */
+ public void setPrimaryColor(int color) {
+ mPrimaryColor = color;
+ invalidate();
+ }
+
+ /**
+ * Changes the secondary color and invalidates the view to force a redraw.
+ * @param color New color to assign to mSecondaryColor.
+ */
+ public void setSecondaryColor(int color) {
+ mSecondaryColor = color;
+ invalidate();
+ }
+
+ /**
+ * Set if we want the thumb to always be filled.
+ * @param alwaysFillThumb Do we want it to always be filled.
+ */
+ public void setAlwaysFillThumb(boolean alwaysFillThumb) {
+ mAlwaysFillThumb = alwaysFillThumb;
+ }
+
/**
* Set the selected value of this Slider.
* @param value The selected value.
@@ -829,7 +970,7 @@ public boolean startAnimation(int fillPercent) {
return true;
}
else {
- mThumbFillPercent = mFillPercent;
+ mThumbFillPercent = mAlwaysFillThumb ? 1 : mFillPercent;
invalidate();
return false;
}
@@ -837,7 +978,7 @@ public boolean startAnimation(int fillPercent) {
public void stopAnimation() {
mRunning = false;
- mThumbFillPercent = mFillPercent;
+ mThumbFillPercent = mAlwaysFillThumb ? 1 : mFillPercent;
if(getHandler() != null)
getHandler().removeCallbacks(this);
invalidate();
@@ -849,7 +990,7 @@ public void run() {
float progress = Math.min(1f, (float)(curTime - mStartTime) / mTransformAnimationDuration);
float value = mInterpolator.getInterpolation(progress);
- mThumbFillPercent = (mFillPercent - mStartFillPercent) * value + mStartFillPercent;
+ mThumbFillPercent = mAlwaysFillThumb ? 1 : ((mFillPercent - mStartFillPercent) * value + mStartFillPercent);
if(progress == 1f)
stopAnimation();
@@ -917,7 +1058,7 @@ public boolean startAnimation(float position) {
public void stopAnimation() {
mRunning = false;
mThumbCurrentRadius = mDiscreteMode && mIsDragging ? 0 : mThumbRadius;
- mThumbFillPercent = mFillPercent;
+ mThumbFillPercent = mAlwaysFillThumb ? 1 : mFillPercent;
mThumbPosition = mPosition;
if(getHandler() != null)
getHandler().removeCallbacks(this);
@@ -933,7 +1074,7 @@ public void run() {
if(mDiscreteMode){
if(mIsDragging) {
mThumbPosition = (mPosition - mStartPosition) * value + mStartPosition;
- mThumbFillPercent = (mFillPercent - mStartFillPercent) * value + mStartFillPercent;
+ mThumbFillPercent = mAlwaysFillThumb ? 1 : ((mFillPercent - mStartFillPercent) * value + mStartFillPercent);
}
else{
float p1 = (float)mTravelAnimationDuration / mDuration;
@@ -942,7 +1083,7 @@ public void run() {
value = mInterpolator.getInterpolation(progress / p1);
mThumbCurrentRadius = mStartRadius * (1f - value);
mThumbPosition = (mPosition - mStartPosition) * value + mStartPosition;
- mThumbFillPercent = (mFillPercent - mStartFillPercent) * value + mStartFillPercent;
+ mThumbFillPercent = mAlwaysFillThumb ? 1 : ((mFillPercent - mStartFillPercent) * value + mStartFillPercent);
}
else if(progress > p2){
mThumbCurrentRadius = mThumbRadius * (progress - p2) / (1 - p2);
@@ -951,7 +1092,7 @@ else if(progress > p2){
}
else{
mThumbPosition = (mPosition - mStartPosition) * value + mStartPosition;
- mThumbFillPercent = (mFillPercent - mStartFillPercent) * value + mStartFillPercent;
+ mThumbFillPercent = mAlwaysFillThumb ? 1 : ((mFillPercent - mStartFillPercent) * value + mStartFillPercent);
if(progress < 0.2)
mThumbCurrentRadius = Math.max(mThumbRadius + mThumbBorderSize * progress * 5, mThumbCurrentRadius);
diff --git a/lib/src/main/java/com/rey/material/widget/SnackBar.java b/lib/src/main/java/com/rey/material/widget/SnackBar.java
index 7659c354..7bdd73dc 100644
--- a/lib/src/main/java/com/rey/material/widget/SnackBar.java
+++ b/lib/src/main/java/com/rey/material/widget/SnackBar.java
@@ -27,11 +27,15 @@
import android.widget.RelativeLayout;
import com.rey.material.R;
+import com.rey.material.app.ThemeManager;
import com.rey.material.drawable.RippleDrawable;
import com.rey.material.util.ThemeUtil;
import com.rey.material.util.ViewUtil;
-public class SnackBar extends FrameLayout {
+public class SnackBar extends FrameLayout implements ThemeManager.OnThemeChangedListener{
+
+ protected int mStyleId;
+ protected int mCurrentStyle = ThemeManager.THEME_UNDEFINED;
private TextView mText;
private Button mAction;
@@ -40,17 +44,18 @@ public class SnackBar extends FrameLayout {
public static final int WRAP_CONTENT = ViewGroup.LayoutParams.WRAP_CONTENT;
private BackgroundDrawable mBackground;
- private int mMarginStart;
- private int mMarginBottom;
- private int mWidth;
- private int mHeight;
+ private int mMarginStart = 0;
+ private int mMarginBottom = 0;
+ private int mWidth = MATCH_PARENT;
+ private int mHeight = WRAP_CONTENT;
private int mMaxHeight;
private int mMinHeight;
- private int mInAnimationId;
- private int mOutAnimationId;
private long mDuration = -1;
private int mActionId;
private boolean mRemoveOnDismiss;
+
+ private Animation mInAnimation;
+ private Animation mOutAnimation;
private Runnable mDismissRunnable = new Runnable() {
@Override
@@ -135,9 +140,9 @@ public SnackBar(Context context, AttributeSet attrs, int defStyleAttr, int defSt
init(context, attrs, defStyleAttr, defStyleRes);
}
- @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
private void init(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){
mText = new TextView(context);
+ mText.setSingleLine(true);
mText.setGravity(Gravity.START|Gravity.CENTER_VERTICAL);
addView(mText, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
@@ -148,7 +153,7 @@ private void init(Context context, AttributeSet attrs, int defStyleAttr, int def
@Override
public void onClick(View v) {
- if(mActionClickListener != null)
+ if (mActionClickListener != null)
mActionClickListener.onActionClick(SnackBar.this, mActionId);
dismiss();
@@ -157,12 +162,169 @@ public void onClick(View v) {
});
addView(mAction, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
-
mBackground = new BackgroundDrawable();
+ mBackground.setColor(0xFF323232);
ViewUtil.setBackground(this, mBackground);
setClickable(true);
applyStyle(context, attrs, defStyleAttr, defStyleRes);
+
+ mStyleId = ThemeManager.getStyleId(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ public SnackBar applyStyle(int resId){
+ ViewUtil.applyStyle(this, resId);
+ applyStyle(getContext(), null, 0, resId);
+ return this;
+ }
+
+ protected void applyStyle(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){
+ TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SnackBar, defStyleAttr, defStyleRes);
+
+ int horizontalPadding = -1;
+ int verticalPadding = -1;
+ int textSize = -1;
+ int textColor = 0;
+ boolean textColorDefined = false;
+ int textAppearance = 0;
+ int actionTextSize = -1;
+ ColorStateList actionTextColor = null;
+ int actionTextAppearance = 0;
+
+ for(int i = 0, count = a.getIndexCount(); i < count; i++){
+ int attr = a.getIndex(i);
+ if(attr == R.styleable.SnackBar_sb_backgroundColor)
+ backgroundColor(a.getColor(attr, 0));
+ else if(attr == R.styleable.SnackBar_sb_backgroundCornerRadius)
+ backgroundRadius(a.getDimensionPixelSize(attr, 0));
+ else if(attr == R.styleable.SnackBar_sb_horizontalPadding)
+ horizontalPadding = a.getDimensionPixelSize(attr, 0);
+ else if(attr == R.styleable.SnackBar_sb_verticalPadding)
+ verticalPadding = a.getDimensionPixelSize(attr, 0);
+ else if(attr == R.styleable.SnackBar_sb_width){
+ if(ThemeUtil.getType(a, attr) == TypedValue.TYPE_INT_DEC)
+ width(a.getInteger(attr, 0));
+ else
+ width(a.getDimensionPixelSize(attr, 0));
+ }
+ else if(attr == R.styleable.SnackBar_sb_height){
+ if(ThemeUtil.getType(a, attr) == TypedValue.TYPE_INT_DEC)
+ height(a.getInteger(attr, 0));
+ else
+ height(a.getDimensionPixelSize(attr, 0));
+ }
+ else if(attr == R.styleable.SnackBar_sb_minWidth)
+ minWidth(a.getDimensionPixelSize(attr, 0));
+ else if(attr == R.styleable.SnackBar_sb_maxWidth)
+ maxWidth(a.getDimensionPixelSize(attr, 0));
+ else if(attr == R.styleable.SnackBar_sb_minHeight)
+ minHeight(a.getDimensionPixelSize(attr, 0));
+ else if(attr == R.styleable.SnackBar_sb_maxHeight)
+ maxHeight(a.getDimensionPixelSize(attr, 0));
+ else if(attr == R.styleable.SnackBar_sb_marginStart)
+ marginStart(a.getDimensionPixelSize(attr, 0));
+ else if(attr == R.styleable.SnackBar_sb_marginBottom)
+ marginBottom(a.getDimensionPixelSize(attr, 0));
+ else if(attr == R.styleable.SnackBar_sb_textSize)
+ textSize = a.getDimensionPixelSize(attr, 0);
+ else if(attr == R.styleable.SnackBar_sb_textColor) {
+ textColor = a.getColor(attr, 0);
+ textColorDefined = true;
+ }
+ else if(attr == R.styleable.SnackBar_sb_textAppearance)
+ textAppearance = a.getResourceId(attr, 0);
+ else if(attr == R.styleable.SnackBar_sb_text)
+ text(a.getString(attr));
+ else if(attr == R.styleable.SnackBar_sb_singleLine)
+ singleLine(a.getBoolean(attr, true));
+ else if(attr == R.styleable.SnackBar_sb_maxLines)
+ maxLines(a.getInteger(attr, 0));
+ else if(attr == R.styleable.SnackBar_sb_lines)
+ lines(a.getInteger(attr, 0));
+ else if(attr == R.styleable.SnackBar_sb_ellipsize){
+ int ellipsize = a.getInteger(attr, 0);
+ switch (ellipsize) {
+ case 1:
+ ellipsize(TruncateAt.START);
+ break;
+ case 2:
+ ellipsize(TruncateAt.MIDDLE);
+ break;
+ case 3:
+ ellipsize(TruncateAt.END);
+ break;
+ case 4:
+ ellipsize(TruncateAt.MARQUEE);
+ break;
+ default:
+ ellipsize(TruncateAt.END);
+ break;
+ }
+ }
+ else if(attr == R.styleable.SnackBar_sb_actionTextSize)
+ actionTextSize = a.getDimensionPixelSize(attr, 0);
+ else if(attr == R.styleable.SnackBar_sb_actionTextColor)
+ actionTextColor = a.getColorStateList(attr);
+ else if(attr == R.styleable.SnackBar_sb_actionTextAppearance)
+ actionTextAppearance = a.getResourceId(attr, 0);
+ else if(attr == R.styleable.SnackBar_sb_actionText)
+ actionText(a.getString(attr));
+ else if(attr == R.styleable.SnackBar_sb_actionRipple)
+ actionRipple(a.getResourceId(attr, 0));
+ else if(attr == R.styleable.SnackBar_sb_duration)
+ duration(a.getInteger(attr, 0));
+ else if(attr == R.styleable.SnackBar_sb_removeOnDismiss)
+ removeOnDismiss(a.getBoolean(attr, true));
+ else if(attr == R.styleable.SnackBar_sb_inAnimation)
+ animationIn(AnimationUtils.loadAnimation(getContext(), a.getResourceId(attr, 0)));
+ else if(attr == R.styleable.SnackBar_sb_outAnimation)
+ animationOut(AnimationUtils.loadAnimation(getContext(), a.getResourceId(attr, 0)));
+ }
+
+ a.recycle();
+
+ if(horizontalPadding >= 0 || verticalPadding >= 0)
+ padding(horizontalPadding >= 0 ? horizontalPadding : mText.getPaddingLeft(), verticalPadding >= 0 ? verticalPadding : mText.getPaddingTop());
+
+ if(textAppearance != 0)
+ textAppearance(textAppearance);
+ if(textSize >= 0)
+ textSize(textSize);
+ if(textColorDefined)
+ textColor(textColor);
+
+ if(textAppearance != 0)
+ actionTextAppearance(actionTextAppearance);
+ if(actionTextSize >= 0)
+ actionTextSize(actionTextSize);
+ if(actionTextColor != null)
+ actionTextColor(actionTextColor);
+
+ }
+
+ @Override
+ public void onThemeChanged(ThemeManager.OnThemeChangedEvent event) {
+ int style = ThemeManager.getInstance().getCurrentStyle(mStyleId);
+ if(mCurrentStyle != style){
+ mCurrentStyle = style;
+ applyStyle(mCurrentStyle);
+ }
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ if(mStyleId != 0) {
+ ThemeManager.getInstance().registerOnThemeChangedListener(this);
+ onThemeChanged(null);
+ }
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ if(mStyleId != 0)
+ ThemeManager.getInstance().unregisterOnThemeChangedListener(this);
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
@@ -182,62 +344,62 @@ public void onRtlPropertiesChanged(int layoutDirection) {
}
@Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- int widthSize = MeasureSpec.getSize(widthMeasureSpec);
- int widthMode = MeasureSpec.getMode(widthMeasureSpec);
- int heightSize = MeasureSpec.getSize(heightMeasureSpec);
- int heightMode = MeasureSpec.getMode(heightMeasureSpec);
- int width;
- int height;
-
- if(mAction.getVisibility() == View.VISIBLE){
- mAction.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), heightMeasureSpec);
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ int widthSize = MeasureSpec.getSize(widthMeasureSpec);
+ int widthMode = MeasureSpec.getMode(widthMeasureSpec);
+ int heightSize = MeasureSpec.getSize(heightMeasureSpec);
+ int heightMode = MeasureSpec.getMode(heightMeasureSpec);
+ int width;
+ int height;
+
+ if(mAction.getVisibility() == View.VISIBLE){
+ mAction.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), heightMeasureSpec);
int padding = mIsRtl ? mText.getPaddingLeft() : mText.getPaddingRight();
mText.measure(MeasureSpec.makeMeasureSpec(widthSize - (mAction.getMeasuredWidth() - padding), widthMode), heightMeasureSpec);
width = mText.getMeasuredWidth() + mAction.getMeasuredWidth() - padding;
- }
- else{
- mText.measure(MeasureSpec.makeMeasureSpec(widthSize, widthMode), heightMeasureSpec);
- width = mText.getMeasuredWidth();
- }
-
- height = Math.max(mText.getMeasuredHeight(), mAction.getMeasuredHeight());
-
- switch (widthMode) {
- case MeasureSpec.AT_MOST:
- width = Math.min(widthSize, width);
- break;
- case MeasureSpec.EXACTLY:
- width = widthSize;
- break;
- }
-
- switch (heightMode) {
- case MeasureSpec.AT_MOST:
- height = Math.min(heightSize, height);
- break;
- case MeasureSpec.EXACTLY:
- height = heightSize;
- break;
- }
+ }
+ else{
+ mText.measure(MeasureSpec.makeMeasureSpec(widthSize, widthMode), heightMeasureSpec);
+ width = mText.getMeasuredWidth();
+ }
+
+ height = Math.max(mText.getMeasuredHeight(), mAction.getMeasuredHeight());
+
+ switch (widthMode) {
+ case MeasureSpec.AT_MOST:
+ width = Math.min(widthSize, width);
+ break;
+ case MeasureSpec.EXACTLY:
+ width = widthSize;
+ break;
+ }
+
+ switch (heightMode) {
+ case MeasureSpec.AT_MOST:
+ height = Math.min(heightSize, height);
+ break;
+ case MeasureSpec.EXACTLY:
+ height = heightSize;
+ break;
+ }
if(mMaxHeight > 0)
height = Math.min(mMaxHeight, height);
if(mMinHeight > 0)
height = Math.max(mMinHeight, height);
-
- setMeasuredDimension(width, height);
- }
-
- @Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- int childLeft = getPaddingLeft();
- int childRight = r - l - getPaddingRight();
- int childTop = getPaddingTop();
- int childBottom = b - t - getPaddingBottom();
-
- if(mAction.getVisibility() == View.VISIBLE){
+
+ setMeasuredDimension(width, height);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ int childLeft = getPaddingLeft();
+ int childRight = r - l - getPaddingRight();
+ int childTop = getPaddingTop();
+ int childBottom = b - t - getPaddingBottom();
+
+ if(mAction.getVisibility() == View.VISIBLE){
if(mIsRtl) {
mAction.layout(childLeft, childTop, childLeft + mAction.getMeasuredWidth(), childBottom);
childLeft += mAction.getMeasuredWidth() - mText.getPaddingLeft();
@@ -246,122 +408,9 @@ protected void onLayout(boolean changed, int l, int t, int r, int b) {
mAction.layout(childRight - mAction.getMeasuredWidth(), childTop, childRight, childBottom);
childRight -= mAction.getMeasuredWidth() - mText.getPaddingRight();
}
- }
+ }
mText.layout(childLeft, childTop, childRight, childBottom);
- }
-
- @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
- private void applyStyle(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){
- TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SnackBar, defStyleAttr, defStyleRes);
-
- int backgroundColor = a.getColor(R.styleable.SnackBar_sb_backgroundColor, 0xFF323232);
- int backgroundRadius = a.getDimensionPixelSize(R.styleable.SnackBar_sb_backgroundCornerRadius, 0);
- int horizontalPadding = a.getDimensionPixelSize(R.styleable.SnackBar_sb_horizontalPadding, ThemeUtil.dpToPx(context, 24));
- int verticalPadding = a.getDimensionPixelSize(R.styleable.SnackBar_sb_verticalPadding, 0);
- TypedValue value = a.peekValue(R.styleable.SnackBar_sb_width);
- if(value != null && value.type == TypedValue.TYPE_INT_DEC)
- mWidth = a.getInteger(R.styleable.SnackBar_sb_width, MATCH_PARENT);
- else
- mWidth = a.getDimensionPixelSize(R.styleable.SnackBar_sb_width, MATCH_PARENT);
- int minWidth = a.getDimensionPixelSize(R.styleable.SnackBar_sb_minWidth, 0);
- int maxWidth = a.getDimensionPixelSize(R.styleable.SnackBar_sb_maxWidth, 0);
- value = a.peekValue(R.styleable.SnackBar_sb_height);
- if(value != null && value.type == TypedValue.TYPE_INT_DEC)
- mHeight = a.getInteger(R.styleable.SnackBar_sb_height, WRAP_CONTENT);
- else
- mHeight = a.getDimensionPixelSize(R.styleable.SnackBar_sb_height, WRAP_CONTENT);
- int minHeight = a.getDimensionPixelSize(R.styleable.SnackBar_sb_minHeight, 0);
- int maxHeight = a.getDimensionPixelSize(R.styleable.SnackBar_sb_maxHeight, 0);
- mMarginStart = a.getDimensionPixelSize(R.styleable.SnackBar_sb_marginStart, 0);
- mMarginBottom = a.getDimensionPixelSize(R.styleable.SnackBar_sb_marginBottom, 0);
- int textSize = a.getDimensionPixelSize(R.styleable.SnackBar_sb_textSize, 0);
- boolean hasTextColor = a.hasValue(R.styleable.SnackBar_sb_textColor);
- int textColor = hasTextColor ? a.getColor(R.styleable.SnackBar_sb_textColor, 0xFFFFFFFF) : 0;
- int textAppearance = a.getResourceId(R.styleable.SnackBar_sb_textAppearance, 0);
- String text = a.getString(R.styleable.SnackBar_sb_text);
- boolean singleLine = a.getBoolean(R.styleable.SnackBar_sb_singleLine, true);
- int maxLines = a.getInteger(R.styleable.SnackBar_sb_maxLines, 0);
- int lines = a.getInteger(R.styleable.SnackBar_sb_lines, 0);
- int ellipsize = a.getInteger(R.styleable.SnackBar_sb_ellipsize, 0);
- int actionTextSize = a.getDimensionPixelSize(R.styleable.SnackBar_sb_actionTextSize, 0);
- ColorStateList actionTextColor;
- value = a.peekValue(R.styleable.SnackBar_sb_actionTextColor);
- if(value != null && value.type >= TypedValue.TYPE_FIRST_COLOR_INT && value.type <= TypedValue.TYPE_LAST_COLOR_INT)
- actionTextColor = ColorStateList.valueOf(a.getColor(R.styleable.SnackBar_sb_actionTextColor, 0xFF000000));
- else
- actionTextColor = a.getColorStateList(R.styleable.SnackBar_sb_actionTextColor);
- int actionTextAppearance = a.getResourceId(R.styleable.SnackBar_sb_actionTextAppearance, 0);
- String actionText = a.getString(R.styleable.SnackBar_sb_actionText);
- int actionRipple = a.getResourceId(R.styleable.SnackBar_sb_actionRipple, 0);
- int duration = a.getInteger(R.styleable.SnackBar_sb_duration, -1);
- mInAnimationId = a.getResourceId(R.styleable.SnackBar_sb_inAnimation, 0);
- mOutAnimationId = a.getResourceId(R.styleable.SnackBar_sb_outAnimation, 0);
- mRemoveOnDismiss = a.getBoolean(R.styleable.SnackBar_sb_removeOnDismiss, true);
-
-
- a.recycle();
-
- backgroundColor(backgroundColor)
- .backgroundRadius(backgroundRadius);
-
- padding(horizontalPadding, verticalPadding);
-
- textAppearance(textAppearance);
- if(textSize > 0)
- textSize(textSize);
- if(hasTextColor)
- textColor(textColor);
- if(text != null)
- text(text);
- singleLine(singleLine);
- if(maxLines > 0)
- maxLines(maxLines);
- if(lines > 0)
- lines(lines);
- if(minWidth > 0)
- minWidth(minWidth);
- if(maxWidth > 0)
- maxWidth(maxWidth);
- if(minHeight > 0)
- minHeight(minHeight);
- if(maxHeight > 0)
- maxHeight(maxHeight);
- switch (ellipsize) {
- case 1:
- ellipsize(TruncateAt.START);
- break;
- case 2:
- ellipsize(TruncateAt.MIDDLE);
- break;
- case 3:
- ellipsize(TruncateAt.END);
- break;
- case 4:
- ellipsize(TruncateAt.MARQUEE);
- break;
- default:
- ellipsize(TruncateAt.END);
- break;
- }
-
- if(textAppearance != 0)
- actionTextAppearance(actionTextAppearance);
- if(actionTextSize > 0)
- actionTextSize(actionTextSize);
- if(actionTextColor != null)
- actionTextColor(actionTextColor);
- if(actionText != null)
- actionText(actionText);
- if(actionRipple != 0)
- actionRipple(actionRipple);
- if(duration >= 0)
- duration(duration);
- }
-
- public SnackBar applyStyle(int resId){
- applyStyle(getContext(), null, 0, resId);
- return this;
}
/**
@@ -707,6 +756,26 @@ public SnackBar stateChangeListener(OnStateChangeListener listener){
return this;
}
+ /**
+ * Set the animation will be shown when SnackBar enter screen.
+ * @param anim The animation.
+ * @return This SnackBar for chaining methods.
+ */
+ public SnackBar animationIn(Animation anim){
+ mInAnimation = anim;
+ return this;
+ }
+
+ /**
+ * Set the animation will be shown when SnackBar exit screen.
+ * @param anim The animation.
+ * @return This SnackBar for chaining methods.
+ */
+ public SnackBar animationOut(Animation anim){
+ mOutAnimation = anim;
+ return this;
+ }
+
/**
* Indicate that this SnackBar should remove itself from parent view after being dismissed.
* @param b
@@ -778,9 +847,10 @@ else if(parent instanceof RelativeLayout){
params.bottomMargin = mMarginBottom;
}
- if(mInAnimationId != 0 && mState != STATE_SHOWN){
- Animation anim = AnimationUtils.loadAnimation(getContext(), mInAnimationId);
- anim.setAnimationListener(new Animation.AnimationListener() {
+ if(mInAnimation != null && mState != STATE_SHOWN){
+ mInAnimation.cancel();
+ mInAnimation.reset();
+ mInAnimation.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
@@ -789,7 +859,8 @@ public void onAnimationStart(Animation animation) {
}
@Override
- public void onAnimationRepeat(Animation animation) {}
+ public void onAnimationRepeat(Animation animation) {
+ }
@Override
public void onAnimationEnd(Animation animation) {
@@ -797,14 +868,16 @@ public void onAnimationEnd(Animation animation) {
startTimer();
}
});
- startAnimation(anim);
+ clearAnimation();
+ startAnimation(mInAnimation);
}
- else{
+ else {
setVisibility(View.VISIBLE);
setState(STATE_SHOWN);
startTimer();
}
}
+
private void startTimer(){
removeCallbacks(mDismissRunnable);
@@ -820,10 +893,11 @@ public void dismiss(){
return;
removeCallbacks(mDismissRunnable);
-
- if(mOutAnimationId != 0){
- Animation anim = AnimationUtils.loadAnimation(getContext(), mOutAnimationId);
- anim.setAnimationListener(new Animation.AnimationListener() {
+
+ if(mOutAnimation != null){
+ mOutAnimation.cancel();
+ mOutAnimation.reset();
+ mOutAnimation.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
@@ -842,7 +916,8 @@ public void onAnimationEnd(Animation animation) {
setVisibility(View.GONE);
}
});
- startAnimation(anim);
+ clearAnimation();
+ startAnimation(mOutAnimation);
}
else{
if(mRemoveOnDismiss && getParent() != null && getParent() instanceof ViewGroup)
diff --git a/lib/src/main/java/com/rey/material/widget/Spinner.java b/lib/src/main/java/com/rey/material/widget/Spinner.java
index c1c1676e..1b3e8348 100644
--- a/lib/src/main/java/com/rey/material/widget/Spinner.java
+++ b/lib/src/main/java/com/rey/material/widget/Spinner.java
@@ -5,20 +5,12 @@
import android.content.res.ColorStateList;
import android.database.DataSetObserver;
import android.graphics.Canvas;
-import android.graphics.ColorFilter;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffColorFilter;
import android.graphics.Rect;
-import android.graphics.Region;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.NonNull;
-import android.support.v4.content.ContextCompat;
-import android.support.v4.view.GravityCompat;
-import android.support.v4.view.ViewCompat;
-import android.support.v4.view.ViewGroupCompat;
import android.support.v7.internal.widget.TintManager;
import android.support.v7.internal.widget.TintTypedArray;
import android.support.v7.internal.widget.ViewUtils;
@@ -35,20 +27,18 @@
import android.view.animation.Interpolator;
import android.widget.AdapterView;
import android.widget.FrameLayout;
-import android.widget.GridView;
import android.widget.ListAdapter;
import android.widget.SpinnerAdapter;
import com.rey.material.R;
+import com.rey.material.app.ThemeManager;
import com.rey.material.drawable.ArrowDrawable;
import com.rey.material.drawable.DividerDrawable;
import com.rey.material.drawable.RippleDrawable;
import com.rey.material.util.ThemeUtil;
+import com.rey.material.util.ViewUtil;
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-
-public class Spinner extends FrameLayout {
+public class Spinner extends FrameLayout implements ThemeManager.OnThemeChangedListener{
private static final int MAX_ITEMS_MEASURED = 15;
@@ -83,8 +73,8 @@ public interface OnItemSelectedListener{
void onItemSelected(Spinner parent, View view, int position, long id);
}
- private boolean mLabelEnable;
- private LabelView mLabelView;
+ private boolean mLabelEnable = false;
+ private android.widget.TextView mLabelView;
private SpinnerAdapter mAdapter;
private OnItemClickListener mOnItemClickListener;
@@ -94,19 +84,19 @@ public interface OnItemSelectedListener{
private int mMinHeight;
private DropdownPopup mPopup;
- private int mDropDownWidth;
+ private int mDropDownWidth = LayoutParams.WRAP_CONTENT;
private ArrowDrawable mArrowDrawable;
- private int mArrowSize;
- private int mArrowPadding;
- private boolean mArrowAnimSwitchMode;
+ private int mArrowSize = 0;
+ private int mArrowPadding = 0;
+ private boolean mArrowAnimSwitchMode = false;
private DividerDrawable mDividerDrawable;
private int mDividerHeight;
private int mDividerPadding;
- private int mGravity;
- private boolean mDisableChildrenWhenDisabled;
+ private int mGravity = Gravity.CENTER;
+ private boolean mDisableChildrenWhenDisabled = false;
private int mSelectedPosition = INVALID_POSITION;
@@ -117,10 +107,12 @@ public interface OnItemSelectedListener{
private DropDownAdapter mTempAdapter;
private SpinnerDataSetObserver mDataSetObserver = new SpinnerDataSetObserver();
-
+
private TintManager mTintManager;
-
+
private RippleManager mRippleManager;
+ protected int mStyleId;
+ protected int mCurrentStyle = ThemeManager.THEME_UNDEFINED;
private boolean mIsRtl = false;
@@ -148,9 +140,12 @@ public Spinner(Context context, AttributeSet attrs, int defStyleAttr, int defSty
init(context, attrs, defStyleAttr, defStyleRes);
}
- public void init(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ private void init(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
setWillNotDraw(false);
+ mPopup = new DropdownPopup(context, attrs, defStyleAttr, defStyleRes);
+ mPopup.setModal(true);
+
applyStyle(context, attrs, defStyleAttr, defStyleRes);
if(isInEditMode()){
@@ -165,119 +160,200 @@ public void onClick(View v) {
showPopup();
}
});
+
+ mStyleId = ThemeManager.getStyleId(context, attrs, defStyleAttr, defStyleRes);
}
public void applyStyle(int resId){
+ ViewUtil.applyStyle(this, resId);
applyStyle(getContext(), null, 0, resId);
}
- private void applyStyle(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){
- CharSequence memoLabel = mLabelView == null ? null : mLabelView.getText();
+ private android.widget.TextView getLabelView(){
+ if(mLabelView == null){
+ mLabelView = new android.widget.TextView(getContext());
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1)
+ mLabelView.setTextDirection(mIsRtl ? TEXT_DIRECTION_RTL : TEXT_DIRECTION_LTR);
+ mLabelView.setSingleLine(true);
+ mLabelView.setDuplicateParentStateEnabled(true);
+ }
+
+ return mLabelView;
+ }
+ protected void applyStyle(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){
removeAllViews();
getRippleManager().onCreate(this, context, attrs, defStyleAttr, defStyleRes);
TintTypedArray a = TintTypedArray.obtainStyledAttributes(context, attrs, R.styleable.Spinner, defStyleAttr, defStyleRes);
- mLabelEnable = a.getBoolean(R.styleable.Spinner_spn_labelEnable, false);
-
- if(mLabelEnable){
- mLabelView = new LabelView(context);
- if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1)
- mLabelView.setTextDirection(mIsRtl ? TEXT_DIRECTION_RTL : TEXT_DIRECTION_LTR);
- mLabelView.setSingleLine(true);
- int labelPadding = a.getDimensionPixelOffset(R.styleable.Spinner_spn_labelPadding, 0);
- int labelTextSize = a.getDimensionPixelSize(R.styleable.Spinner_spn_labelTextSize, 0);
- ColorStateList labelTextColor = a.getColorStateList(R.styleable.Spinner_spn_labelTextColor);
- int labelTextAppearance = a.getResourceId(R.styleable.Spinner_spn_labelTextAppearance, 0);
- int labelEllipsize = a.getInteger(R.styleable.Spinner_spn_labelEllipsize, 0);
- CharSequence label = ThemeUtil.getString(a, R.styleable.Spinner_spn_label, memoLabel);
-
- mLabelView.setText(label);
- mLabelView.setPadding(0, 0, 0, labelPadding);
- if(labelTextAppearance > 0)
- mLabelView.setTextAppearance(context, labelTextAppearance);
- if(labelTextSize > 0)
- mLabelView.setTextSize(TypedValue.COMPLEX_UNIT_PX, labelTextSize);
- if(labelTextColor != null)
- mLabelView.setTextColor(labelTextColor);
-
- switch (labelEllipsize) {
- case 1:
- mLabelView.setEllipsize(TextUtils.TruncateAt.START);
- break;
- case 2:
- mLabelView.setEllipsize(TextUtils.TruncateAt.MIDDLE);
- break;
- case 3:
- mLabelView.setEllipsize(TextUtils.TruncateAt.END);
- break;
- case 4:
- mLabelView.setEllipsize(TextUtils.TruncateAt.MARQUEE);
- break;
- default:
- mLabelView.setEllipsize(TextUtils.TruncateAt.END);
- break;
+ int arrowAnimDuration = -1;
+ ColorStateList arrowColor = null;
+ Interpolator arrowInterpolator = null;
+ boolean arrowClockwise = true;
+ int dividerAnimDuration = -1;
+ ColorStateList dividerColor = null;
+ ColorStateList labelTextColor = null;
+ int labelTextSize = -1;
+
+ for(int i = 0, count = a.getIndexCount(); i < count; i++){
+ int attr = a.getIndex(i);
+
+ if(attr == R.styleable.Spinner_spn_labelEnable)
+ mLabelEnable = a.getBoolean(attr, false);
+ else if(attr == R.styleable.Spinner_spn_labelPadding)
+ getLabelView().setPadding(0, 0, 0, a.getDimensionPixelSize(attr, 0));
+ else if (attr == R.styleable.Spinner_spn_labelTextSize)
+ labelTextSize = a.getDimensionPixelSize(attr, 0);
+ else if(attr == R.styleable.Spinner_spn_labelTextColor)
+ labelTextColor = a.getColorStateList(attr);
+ else if(attr == R.styleable.Spinner_spn_labelTextAppearance)
+ getLabelView().setTextAppearance(context, a.getResourceId(attr, 0));
+ else if(attr == R.styleable.Spinner_spn_labelEllipsize){
+ int labelEllipsize = a.getInteger(attr, 0);
+ switch (labelEllipsize) {
+ case 1:
+ getLabelView().setEllipsize(TextUtils.TruncateAt.START);
+ break;
+ case 2:
+ getLabelView().setEllipsize(TextUtils.TruncateAt.MIDDLE);
+ break;
+ case 3:
+ getLabelView().setEllipsize(TextUtils.TruncateAt.END);
+ break;
+ case 4:
+ getLabelView().setEllipsize(TextUtils.TruncateAt.MARQUEE);
+ break;
+ default:
+ getLabelView().setEllipsize(TextUtils.TruncateAt.END);
+ break;
+ }
+ }
+ else if(attr == R.styleable.Spinner_spn_label)
+ getLabelView().setText(a.getString(attr));
+ else if(attr == R.styleable.Spinner_android_gravity)
+ mGravity = a.getInt(attr, 0);
+ else if(attr == R.styleable.Spinner_android_minWidth)
+ setMinimumWidth(a.getDimensionPixelOffset(attr, 0));
+ else if(attr == R.styleable.Spinner_android_minHeight)
+ setMinimumHeight(a.getDimensionPixelOffset(attr, 0));
+ else if(attr == R.styleable.Spinner_android_dropDownWidth)
+ mDropDownWidth = a.getLayoutDimension(attr, LayoutParams.WRAP_CONTENT);
+ else if(attr == R.styleable.Spinner_android_popupBackground)
+ mPopup.setBackgroundDrawable(a.getDrawable(attr));
+ else if(attr == R.styleable.Spinner_prompt)
+ mPopup.setPromptText(a.getString(attr));
+ else if(attr == R.styleable.Spinner_spn_popupItemAnimation)
+ mPopup.setItemAnimation(a.getResourceId(attr, 0));
+ else if(attr == R.styleable.Spinner_spn_popupItemAnimOffset)
+ mPopup.setItemAnimationOffset(a.getInteger(attr, 0));
+ else if(attr == R.styleable.Spinner_disableChildrenWhenDisabled)
+ mDisableChildrenWhenDisabled = a.getBoolean(attr, false);
+ else if(attr == R.styleable.Spinner_spn_arrowSwitchMode)
+ mArrowAnimSwitchMode = a.getBoolean(attr, false);
+ else if(attr == R.styleable.Spinner_spn_arrowAnimDuration)
+ arrowAnimDuration = a.getInteger(attr, 0);
+ else if(attr == R.styleable.Spinner_spn_arrowSize)
+ mArrowSize = a.getDimensionPixelSize(attr, 0);
+ else if(attr == R.styleable.Spinner_spn_arrowPadding)
+ mArrowPadding = a.getDimensionPixelSize(attr, 0);
+ else if(attr == R.styleable.Spinner_spn_arrowColor)
+ arrowColor = a.getColorStateList(attr);
+ else if(attr == R.styleable.Spinner_spn_arrowInterpolator){
+ int resId = a.getResourceId(attr, 0);
+ arrowInterpolator = AnimationUtils.loadInterpolator(context, resId);
}
- addView(mLabelView, 0, new ViewGroup.LayoutParams(LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
+ else if(attr == R.styleable.Spinner_spn_arrowAnimClockwise)
+ arrowClockwise = a.getBoolean(attr, true);
+ else if(attr == R.styleable.Spinner_spn_dividerHeight)
+ mDividerHeight = a.getDimensionPixelOffset(attr, 0);
+ else if(attr == R.styleable.Spinner_spn_dividerPadding)
+ mDividerPadding = a.getDimensionPixelOffset(attr, 0);
+ else if(attr == R.styleable.Spinner_spn_dividerAnimDuration)
+ dividerAnimDuration = a.getInteger(attr, 0);
+ else if(attr == R.styleable.Spinner_spn_dividerColor)
+ dividerColor = a.getColorStateList(attr);
}
- mGravity = a.getInt(R.styleable.Spinner_android_gravity, Gravity.CENTER);
+ a.recycle();
- setMinimumWidth(a.getDimensionPixelOffset(R.styleable.Spinner_android_minWidth, 0));
- setMinimumHeight(a.getDimensionPixelOffset(R.styleable.Spinner_android_minHeight, 0));
+ if(labelTextColor != null)
+ getLabelView().setTextColor(labelTextColor);
- mPopup = new DropdownPopup(context, attrs, defStyleAttr, defStyleRes);
- mPopup.setModal(true);
- mDropDownWidth = a.getLayoutDimension(R.styleable.Spinner_android_dropDownWidth, LayoutParams.WRAP_CONTENT);
- mPopup.setBackgroundDrawable(a.getDrawable(R.styleable.Spinner_android_popupBackground));
- mPopup.setPromptText(a.getString(R.styleable.Spinner_prompt));
- mPopup.setItemAnimation(a.getResourceId(R.styleable.Spinner_spn_popupItemAnimation, 0));
- mPopup.setItemAnimationOffset(a.getInteger(R.styleable.Spinner_spn_popupItemAnimOffset, 50));
- mDisableChildrenWhenDisabled = a.getBoolean(R.styleable.Spinner_disableChildrenWhenDisabled, false);
-
- mArrowAnimSwitchMode = a.getBoolean(R.styleable.Spinner_spn_arrowSwitchMode, false);
- int arrowAnimDuration = a.getInteger(R.styleable.Spinner_spn_arrowAnimDuration, 0);
- mArrowSize = a.getDimensionPixelSize(R.styleable.Spinner_spn_arrowSize, ThemeUtil.dpToPx(getContext(), 4));
- mArrowPadding = a.getDimensionPixelSize(R.styleable.Spinner_spn_arrowPadding, ThemeUtil.dpToPx(getContext(), 4));
- ColorStateList arrowColor = a.getColorStateList(R.styleable.Spinner_spn_arrowColor);
- if(arrowColor == null)
- arrowColor = ColorStateList.valueOf(ThemeUtil.colorControlNormal(context, 0xFF000000));
- int resId = a.getResourceId(R.styleable.Spinner_spn_arrowInterpolator, 0);
- Interpolator arrowInterpolator = resId != 0 ? AnimationUtils.loadInterpolator(context, resId) : null;
- boolean arrowClockwise = a.getBoolean(R.styleable.Spinner_spn_arrowAnimClockwise, true);
-
- if(mArrowSize > 0) {
- mArrowDrawable = new ArrowDrawable(ArrowDrawable.MODE_DOWN, mArrowSize, arrowColor, arrowAnimDuration, arrowInterpolator, arrowClockwise);
- mArrowDrawable.setCallback(this);
- }
+ if(labelTextSize >= 0)
+ getLabelView().setTextSize(TypedValue.COMPLEX_UNIT_PX, labelTextSize);
- mDividerHeight = a.getDimensionPixelOffset(R.styleable.Spinner_spn_dividerHeight, 0);
- mDividerPadding = a.getDimensionPixelOffset(R.styleable.Spinner_spn_dividerPadding, 0);
- int dividerAnimDuration = a.getInteger(R.styleable.Spinner_spn_dividerAnimDuration, 0);
- ColorStateList dividerColor = a.getColorStateList(R.styleable.Spinner_spn_dividerColor);
- if(dividerColor == null){
- int[][] states = new int[][]{
- new int[]{-android.R.attr.state_pressed},
- new int[]{android.R.attr.state_pressed, android.R.attr.state_enabled},
- };
- int[] colors = new int[]{
- ThemeUtil.colorControlNormal(context, 0xFF000000),
- ThemeUtil.colorControlActivated(context, 0xFF000000),
- };
-
- dividerColor = new ColorStateList(states, colors);
+ if(mLabelEnable)
+ addView(getLabelView(), 0, new ViewGroup.LayoutParams(LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
+
+ if(mArrowSize > 0){
+ if(mArrowDrawable == null){
+ if(arrowColor == null)
+ arrowColor = ColorStateList.valueOf(ThemeUtil.colorControlNormal(context, 0xFF000000));
+
+ if(arrowAnimDuration < 0)
+ arrowAnimDuration = 0;
+
+ mArrowDrawable = new ArrowDrawable(ArrowDrawable.MODE_DOWN, mArrowSize, arrowColor, arrowAnimDuration, arrowInterpolator, arrowClockwise);
+ mArrowDrawable.setCallback(this);
+ }
+ else{
+ mArrowDrawable.setArrowSize(mArrowSize);
+ mArrowDrawable.setClockwise(arrowClockwise);
+
+ if(arrowColor != null)
+ mArrowDrawable.setColor(arrowColor);
+
+ if(arrowAnimDuration >= 0)
+ mArrowDrawable.setAnimationDuration(arrowAnimDuration);
+
+ if(arrowInterpolator != null)
+ mArrowDrawable.setInterpolator(arrowInterpolator);
+ }
+ }
+ else if(mArrowDrawable != null){
+ mArrowDrawable.setCallback(null);
+ mArrowDrawable = null;
}
if(mDividerHeight > 0){
- mDividerDrawable = new DividerDrawable(mDividerHeight, dividerColor, dividerAnimDuration);
- mDividerDrawable.setCallback(this);
+ if(mDividerDrawable == null){
+ if(dividerAnimDuration < 0)
+ dividerAnimDuration = 0;
+
+ if(dividerColor == null){
+ int[][] states = new int[][]{
+ new int[]{-android.R.attr.state_pressed},
+ new int[]{android.R.attr.state_pressed, android.R.attr.state_enabled},
+ };
+ int[] colors = new int[]{
+ ThemeUtil.colorControlNormal(context, 0xFF000000),
+ ThemeUtil.colorControlActivated(context, 0xFF000000),
+ };
+
+ dividerColor = new ColorStateList(states, colors);
+ }
+
+ mDividerDrawable = new DividerDrawable(mDividerHeight, dividerColor, dividerAnimDuration);
+ mDividerDrawable.setCallback(this);
+ }
+ else{
+ mDividerDrawable.setDividerHeight(mDividerHeight);
+
+ if(dividerColor != null)
+ mDividerDrawable.setColor(dividerColor);
+
+ if(dividerAnimDuration >= 0)
+ mDividerDrawable.setAnimationDuration(dividerAnimDuration);
+ }
+ }
+ else if(mDividerDrawable != null){
+ mDividerDrawable.setCallback(null);
+ mDividerDrawable = null;
}
mTintManager = a.getTintManager();
- a.recycle();
-
if (mTempAdapter != null) {
mPopup.setAdapter(mTempAdapter);
mTempAdapter = null;
@@ -285,6 +361,26 @@ private void applyStyle(Context context, AttributeSet attrs, int defStyleAttr, i
if(mAdapter != null)
setAdapter(mAdapter);
+
+ requestLayout();
+ }
+
+ @Override
+ public void onThemeChanged(ThemeManager.OnThemeChangedEvent event) {
+ int style = ThemeManager.getInstance().getCurrentStyle(mStyleId);
+ if(mCurrentStyle != style){
+ mCurrentStyle = style;
+ applyStyle(mCurrentStyle);
+ }
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ if(mStyleId != 0) {
+ ThemeManager.getInstance().registerOnThemeChangedListener(this);
+ onThemeChanged(null);
+ }
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
@@ -528,9 +624,13 @@ public int getBaseline() {
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
+ mRippleManager.cancelRipple(this);
if (mPopup != null && mPopup.isShowing())
- mPopup.dismiss();
+ mPopup.dismiss();
+
+ if(mStyleId != 0)
+ ThemeManager.getInstance().unregisterOnThemeChangedListener(this);
}
@Override
@@ -1118,19 +1218,6 @@ public void unregisterDataSetObserver(DataSetObserver observer) {
}
}
- private class LabelView extends android.widget.TextView{
-
- public LabelView(Context context) {
- super(context);
- }
-
- @Override
- protected int[] onCreateDrawableState(int extraSpace) {
- return Spinner.this.getDrawableState();
- }
-
- }
-
private class DropdownPopup extends ListPopupWindow {
private CharSequence mHintText;
diff --git a/lib/src/main/java/com/rey/material/widget/Switch.java b/lib/src/main/java/com/rey/material/widget/Switch.java
index 54366c76..23201663 100644
--- a/lib/src/main/java/com/rey/material/widget/Switch.java
+++ b/lib/src/main/java/com/rey/material/widget/Switch.java
@@ -10,7 +10,6 @@
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.drawable.Drawable;
-import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.SystemClock;
@@ -26,15 +25,18 @@
import android.widget.Checkable;
import com.rey.material.R;
+import com.rey.material.app.ThemeManager;
import com.rey.material.drawable.RippleDrawable;
import com.rey.material.util.ColorUtil;
import com.rey.material.util.ThemeUtil;
import com.rey.material.util.ViewUtil;
-public class Switch extends View implements Checkable {
+public class Switch extends View implements Checkable, ThemeManager.OnThemeChangedListener {
private RippleManager mRippleManager;
-
+ protected int mStyleId;
+ protected int mCurrentStyle = ThemeManager.THEME_UNDEFINED;
+
private boolean mRunning = false;
private Paint mPaint;
@@ -42,16 +44,16 @@ public class Switch extends View implements Checkable {
private RectF mTempRect;
private Path mTrackPath;
- private int mTrackSize;
+ private int mTrackSize = -1;
private ColorStateList mTrackColors;
- private Paint.Cap mTrackCap;
- private int mThumbRadius;
+ private Paint.Cap mTrackCap = Paint.Cap.ROUND;
+ private int mThumbRadius = -1;
private ColorStateList mThumbColors;
private float mThumbPosition;
- private int mMaxAnimDuration;
+ private int mMaxAnimDuration = -1;
private Interpolator mInterpolator;
private int mGravity = Gravity.CENTER_VERTICAL;
-
+
private boolean mChecked = false;
private float mMemoX;
@@ -64,8 +66,8 @@ public class Switch extends View implements Checkable {
private int[] mTempStates = new int[2];
- private int mShadowSize;
- private int mShadowOffset;
+ private int mShadowSize = -1;
+ private int mShadowOffset = -1;
private Path mShadowPath;
private Paint mShadowPaint;
@@ -122,39 +124,75 @@ private void init(Context context, AttributeSet attrs, int defStyleAttr, int def
mFlingVelocity = ViewConfiguration.get(context).getScaledMinimumFlingVelocity();
applyStyle(context, attrs, defStyleAttr, defStyleRes);
+
+ mStyleId = ThemeManager.getStyleId(context, attrs, defStyleAttr, defStyleRes);
}
public void applyStyle(int resId){
+ ViewUtil.applyStyle(this, resId);
applyStyle(getContext(), null, 0, resId);
}
- private void applyStyle(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){
+ protected void applyStyle(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){
getRippleManager().onCreate(this, context, attrs, defStyleAttr, defStyleRes);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Switch, defStyleAttr, defStyleRes);
- mTrackSize = a.getDimensionPixelSize(R.styleable.Switch_sw_trackSize, ThemeUtil.dpToPx(context, 2));
- mTrackColors = a.getColorStateList(R.styleable.Switch_sw_trackColor);
- int cap = a.getInteger(R.styleable.Switch_sw_trackCap, 0);
- if(cap == 0)
- mTrackCap = Paint.Cap.BUTT;
- else if(cap == 1)
- mTrackCap = Paint.Cap.ROUND;
- else
- mTrackCap = Paint.Cap.SQUARE;
- mThumbColors = a.getColorStateList(R.styleable.Switch_sw_thumbColor);
- mThumbRadius = a.getDimensionPixelSize(R.styleable.Switch_sw_thumbRadius, ThemeUtil.dpToPx(context, 8));
- mShadowSize = a.getDimensionPixelSize(R.styleable.Switch_sw_thumbElevation, ThemeUtil.dpToPx(context, 2));
- mShadowOffset = mShadowSize / 2;
- mMaxAnimDuration = a.getInt(R.styleable.Switch_sw_animDuration, context.getResources().getInteger(android.R.integer.config_mediumAnimTime));
- mGravity = a.getInt(R.styleable.Switch_android_gravity, Gravity.CENTER_VERTICAL);
- mChecked = a.getBoolean(R.styleable.Switch_android_checked, false);
- mThumbPosition = mChecked ? 1f : 0f;
- int resId = a.getResourceId(R.styleable.Switch_sw_interpolator, 0);
- mInterpolator = resId != 0 ? AnimationUtils.loadInterpolator(context, resId) : new DecelerateInterpolator();
+ for(int i = 0, count = a.getIndexCount(); i < count; i++){
+ int attr = a.getIndex(i);
+ if(attr == R.styleable.Switch_sw_trackSize)
+ mTrackSize = a.getDimensionPixelSize(attr, 0);
+ else if(attr == R.styleable.Switch_sw_trackColor)
+ mTrackColors = a.getColorStateList(attr);
+ else if(attr == R.styleable.Switch_sw_trackCap){
+ int cap = a.getInteger(attr, 0);
+ if(cap == 0)
+ mTrackCap = Paint.Cap.BUTT;
+ else if(cap == 1)
+ mTrackCap = Paint.Cap.ROUND;
+ else
+ mTrackCap = Paint.Cap.SQUARE;
+ }
+ else if(attr == R.styleable.Switch_sw_thumbColor)
+ mThumbColors = a.getColorStateList(attr);
+ else if(attr == R.styleable.Switch_sw_thumbRadius)
+ mThumbRadius = a.getDimensionPixelSize(attr, 0);
+ else if(attr == R.styleable.Switch_sw_thumbElevation) {
+ mShadowSize = a.getDimensionPixelSize(attr, 0);
+ mShadowOffset = mShadowSize / 2;
+ }
+ else if(attr == R.styleable.Switch_sw_animDuration)
+ mMaxAnimDuration = a.getInt(attr, 0);
+ else if(attr == R.styleable.Switch_android_gravity)
+ mGravity = a.getInt(attr, 0);
+ else if(attr == R.styleable.Switch_android_checked)
+ setCheckedImmediately(a.getBoolean(attr, mChecked));
+ else if(attr == R.styleable.Switch_sw_interpolator){
+ int resId = a.getResourceId(R.styleable.Switch_sw_interpolator, 0);
+ if(resId != 0)
+ mInterpolator = AnimationUtils.loadInterpolator(context, resId);
+ }
+ }
a.recycle();
+ if(mTrackSize < 0)
+ mTrackSize = ThemeUtil.dpToPx(context, 2);
+
+ if(mThumbRadius < 0)
+ mThumbRadius = ThemeUtil.dpToPx(context, 8);
+
+ if(mShadowSize < 0) {
+ mShadowSize = ThemeUtil.dpToPx(context, 2);
+ mShadowOffset = mShadowSize / 2;
+ }
+
+ if(mMaxAnimDuration < 0)
+ mMaxAnimDuration = context.getResources().getInteger(android.R.integer.config_mediumAnimTime);
+
+ if(mInterpolator == null)
+ mInterpolator = new DecelerateInterpolator();
+
if(mTrackColors == null){
int[][] states = new int[][]{
new int[]{-android.R.attr.state_checked},
@@ -182,8 +220,34 @@ else if(cap == 1)
}
mPaint.setStrokeCap(mTrackCap);
-
buildShadow();
+ invalidate();
+ }
+
+ @Override
+ public void onThemeChanged(ThemeManager.OnThemeChangedEvent event) {
+ int style = ThemeManager.getInstance().getCurrentStyle(mStyleId);
+ if(mCurrentStyle != style){
+ mCurrentStyle = style;
+ applyStyle(mCurrentStyle);
+ }
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ if(mStyleId != 0) {
+ ThemeManager.getInstance().registerOnThemeChangedListener(this);
+ onThemeChanged(null);
+ }
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ mRippleManager.cancelRipple(this);
+ if(mStyleId != 0)
+ ThemeManager.getInstance().unregisterOnThemeChangedListener(this);
}
@Override
@@ -374,7 +438,7 @@ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
}
}
- private int getTrackColor(boolean checked){
+ private int getTrackColor(boolean checked){
mTempStates[0] = isEnabled() ? android.R.attr.state_enabled : -android.R.attr.state_enabled;
mTempStates[1] = checked ? android.R.attr.state_checked : -android.R.attr.state_checked;
diff --git a/lib/src/main/java/com/rey/material/widget/TabPageIndicator.java b/lib/src/main/java/com/rey/material/widget/TabPageIndicator.java
index e833ecfe..e2a2727a 100644
--- a/lib/src/main/java/com/rey/material/widget/TabPageIndicator.java
+++ b/lib/src/main/java/com/rey/material/widget/TabPageIndicator.java
@@ -13,30 +13,37 @@
import android.text.TextUtils.TruncateAt;
import android.util.AttributeSet;
import android.view.Gravity;
+import android.view.View;
import android.view.ViewGroup;
+import android.widget.FrameLayout;
import android.widget.HorizontalScrollView;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.rey.material.R;
+import com.rey.material.app.ThemeManager;
import com.rey.material.drawable.RippleDrawable;
import com.rey.material.util.ThemeUtil;
import com.rey.material.util.ViewUtil;
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
-public class TabPageIndicator extends HorizontalScrollView implements ViewPager.OnPageChangeListener, android.view.View.OnClickListener{
+public class TabPageIndicator extends HorizontalScrollView implements ViewPager.OnPageChangeListener, android.view.View.OnClickListener, ThemeManager.OnThemeChangedListener{
- private LinearLayout mTabContainer;
+ protected int mStyleId;
+ protected int mCurrentStyle = ThemeManager.THEME_UNDEFINED;
+
+ private TabContainerLayout mTabContainer;
private ViewPager mViewPager;
private int mMode;
- private int mTabPadding;
- private int mTabRippleStyle;
- private int mTextAppearance;
+ private int mTabPadding = -1;
+ private int mTabRippleStyle = 0;
+ private int mTextAppearance = 0;
+ private boolean mTabSingleLine = true;
private int mIndicatorOffset;
private int mIndicatorWidth;
- private int mIndicatorHeight;
+ private int mIndicatorHeight = -1;
private Paint mPaint;
@@ -45,6 +52,7 @@ public class TabPageIndicator extends HorizontalScrollView implements ViewPager.
private int mSelectedPosition;
private boolean mScrolling = false;
+ private boolean mIsRtl = false;
private Runnable mTabAnimSelector;
@@ -93,44 +101,97 @@ private void init(Context context, AttributeSet attrs, int defStyleAttr, int def
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setStyle(Paint.Style.FILL);
+ mPaint.setColor(ThemeUtil.colorAccent(context, 0xFFFFFFFF));
- mTabContainer = new LinearLayout(context);
- mTabContainer.setOrientation(LinearLayout.HORIZONTAL);
- mTabContainer.setGravity(Gravity.CENTER);
+ mTabContainer = new TabContainerLayout(context);
applyStyle(context, attrs, defStyleAttr, defStyleRes);
if(isInEditMode())
addTemporaryTab();
+
+ mStyleId = ThemeManager.getStyleId(context, attrs, defStyleAttr, defStyleRes);
}
public void applyStyle(int resId){
+ ViewUtil.applyStyle(this, resId);
applyStyle(getContext(), null, 0, resId);
}
- private void applyStyle(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){
+ protected void applyStyle(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TabPageIndicator, defStyleAttr, defStyleRes);
- mTabPadding = a.getDimensionPixelSize(R.styleable.TabPageIndicator_tpi_tabPadding, ThemeUtil.dpToPx(context, 12));
- mTabRippleStyle = a.getResourceId(R.styleable.TabPageIndicator_tpi_tabRipple, 0);
- int indicatorColor = a.getColor(R.styleable.TabPageIndicator_tpi_indicatorColor, ThemeUtil.colorAccent(context, 0xFFFFFFFF));
- mIndicatorHeight = a.getDimensionPixelSize(R.styleable.TabPageIndicator_tpi_indicatorHeight, ThemeUtil.dpToPx(context, 2));
- mTextAppearance = a.getResourceId(R.styleable.TabPageIndicator_android_textAppearance, 0);
- mMode = a.getInteger(R.styleable.TabPageIndicator_tpi_mode, MODE_SCROLL);
+ int textAppearance = 0;
+ int mode = -1;
+ int rippleStyle = 0;
+
+ for(int i = 0, count = a.getIndexCount(); i < count; i++){
+ int attr = a.getIndex(i);
+ if(attr == R.styleable.TabPageIndicator_tpi_tabPadding)
+ mTabPadding = a.getDimensionPixelSize(attr, 0);
+ else if(attr == R.styleable.TabPageIndicator_tpi_tabRipple)
+ rippleStyle = a.getResourceId(attr, 0);
+ else if(attr == R.styleable.TabPageIndicator_tpi_indicatorColor)
+ mPaint.setColor(a.getColor(attr, 0));
+ else if(attr == R.styleable.TabPageIndicator_tpi_indicatorHeight)
+ mIndicatorHeight = a.getDimensionPixelSize(attr, 0);
+ else if(attr == R.styleable.TabPageIndicator_tpi_tabSingleLine)
+ mTabSingleLine = a.getBoolean(attr, true);
+ else if(attr == R.styleable.TabPageIndicator_android_textAppearance)
+ textAppearance = a.getResourceId(attr, 0);
+ else if(attr == R.styleable.TabPageIndicator_tpi_mode)
+ mode = a.getInteger(attr, 0);
+ }
a.recycle();
- removeAllViews();
- if(mMode == MODE_SCROLL) {
- addView(mTabContainer, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT));
- setFillViewport(false);
+ if(mTabPadding < 0)
+ mTabPadding = ThemeUtil.dpToPx(context, 12);
+
+ if(mIndicatorHeight < 0)
+ mIndicatorHeight = ThemeUtil.dpToPx(context, 2);
+
+ if(mode >= 0){
+ if(mMode != mode || getChildCount() == 0){
+ mMode = mode;
+ removeAllViews();
+ if(mMode == MODE_SCROLL) {
+ addView(mTabContainer, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT));
+ setFillViewport(false);
+ }
+ else if(mMode == MODE_FIXED){
+ addView(mTabContainer, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
+ setFillViewport(true);
+ }
+ }
}
- else if(mMode == MODE_FIXED){
- addView(mTabContainer, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
- setFillViewport(true);
+
+ if(textAppearance != 0 && mTextAppearance != textAppearance){
+ mTextAppearance = textAppearance;
+ for(int i = 0, count = mTabContainer.getChildCount(); i < count; i++){
+ CheckedTextView tv = (CheckedTextView)mTabContainer.getChildAt(i);
+ tv.setTextAppearance(context, mTextAppearance);
+ }
+ }
+
+ if(rippleStyle != 0 && rippleStyle != mTabRippleStyle){
+ mTabRippleStyle = rippleStyle;
+ for(int i = 0, count = mTabContainer.getChildCount(); i < count; i++)
+ ViewUtil.setBackground(mTabContainer.getChildAt(i), new RippleDrawable.Builder(getContext(), mTabRippleStyle).build());
}
- mPaint.setColor(indicatorColor);
+ if(mViewPager != null)
+ notifyDataSetChanged();
+ requestLayout();
+ }
+
+ @Override
+ public void onThemeChanged(ThemeManager.OnThemeChangedEvent event) {
+ int style = ThemeManager.getInstance().getCurrentStyle(mStyleId);
+ if(mCurrentStyle != style){
+ mCurrentStyle = style;
+ applyStyle(mCurrentStyle);
+ }
}
@Override
@@ -138,23 +199,96 @@ public void onAttachedToWindow() {
super.onAttachedToWindow();
// Re-post the selector we saved
if (mTabAnimSelector != null)
- post(mTabAnimSelector);
+ post(mTabAnimSelector);
+
+ if(mStyleId != 0) {
+ ThemeManager.getInstance().registerOnThemeChangedListener(this);
+ onThemeChanged(null);
+ }
}
@Override
public void onDetachedFromWindow() {
super.onDetachedFromWindow();
if (mTabAnimSelector != null)
- removeCallbacks(mTabAnimSelector);
+ removeCallbacks(mTabAnimSelector);
+
+ if(mStyleId != 0)
+ ThemeManager.getInstance().unregisterOnThemeChangedListener(this);
}
-
+
+ @Override
+ public void onRtlPropertiesChanged(int layoutDirection) {
+ boolean rtl = layoutDirection == LAYOUT_DIRECTION_RTL;
+ if(mIsRtl != rtl) {
+ mIsRtl = rtl;
+ invalidate();
+ }
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ int widthMode = MeasureSpec.getMode(widthMeasureSpec);
+ int widthSize = MeasureSpec.getSize(widthMeasureSpec);
+ int heightMode = MeasureSpec.getMode(heightMeasureSpec);
+ int heightSize = MeasureSpec.getSize(heightMeasureSpec);
+
+ int ws = widthMeasureSpec;
+ if(ws != MeasureSpec.UNSPECIFIED)
+ ws = MeasureSpec.makeMeasureSpec(widthSize - getPaddingLeft() - getPaddingRight(), widthMode);
+
+ int hs = heightMeasureSpec;
+ if(heightMode != MeasureSpec.UNSPECIFIED)
+ hs = MeasureSpec.makeMeasureSpec(heightSize - getPaddingTop() - getPaddingBottom(), heightMode);
+
+ mTabContainer.measure(ws, hs);
+
+ int width = 0;
+ switch (widthMode){
+ case MeasureSpec.UNSPECIFIED:
+ width = mTabContainer.getMeasuredWidth() + getPaddingLeft() + getPaddingRight();
+ break;
+ case MeasureSpec.AT_MOST:
+ width = Math.min(mTabContainer.getMeasuredWidth() + getPaddingLeft() + getPaddingRight(), widthSize);
+ break;
+ case MeasureSpec.EXACTLY:
+ width = widthSize;
+ break;
+ }
+
+ int height = 0;
+ switch (heightMode){
+ case MeasureSpec.UNSPECIFIED:
+ height = mTabContainer.getMeasuredHeight() + getPaddingTop() + getPaddingBottom();
+ break;
+ case MeasureSpec.AT_MOST:
+ height = Math.min(mTabContainer.getMeasuredHeight() + getPaddingTop() + getPaddingBottom(), heightSize);
+ break;
+ case MeasureSpec.EXACTLY:
+ height = heightSize;
+ break;
+ }
+
+ if(mTabContainer.getMeasuredWidth() != width - getPaddingLeft() - getPaddingRight() || mTabContainer.getMeasuredHeight() != height - getPaddingTop() - getPaddingBottom())
+ mTabContainer.measure(MeasureSpec.makeMeasureSpec(width - getPaddingLeft() - getPaddingRight(), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(height - getPaddingTop() - getPaddingBottom(), MeasureSpec.EXACTLY));
+
+ setMeasuredDimension(width, height);
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+ TextView tv = getTabView(mSelectedPosition);
+ if(tv != null)
+ updateIndicator(tv.getLeft(), tv.getMeasuredWidth());
+ }
+
private CheckedTextView getTabView(int position){
return (CheckedTextView)mTabContainer.getChildAt(position);
}
private void animateToTab(final int position) {
- final CheckedTextView tv = getTabView(position);
- if(tv == null)
+ if(getTabView(position) == null)
return;
if (mTabAnimSelector != null)
@@ -162,8 +296,10 @@ private void animateToTab(final int position) {
mTabAnimSelector = new Runnable() {
public void run() {
- if(!mScrolling)
- updateIndicator(tv.getLeft(), tv.getWidth());
+ CheckedTextView tv = getTabView(position);
+ if(!mScrolling) {
+ updateIndicator(tv.getLeft(), tv.getMeasuredWidth());
+ }
smoothScrollTo(tv.getLeft() - (getWidth() - tv.getWidth()) / 2 + getPaddingLeft(), 0);
mTabAnimSelector = null;
@@ -190,7 +326,7 @@ public void setViewPager(ViewPager view) {
return;
if (mViewPager != null){
- mViewPager.setOnPageChangeListener(null);
+ mViewPager.removeOnPageChangeListener(this);
PagerAdapter adapter = view.getAdapter();
if(adapter != null)
adapter.unregisterDataSetObserver(mObserver);
@@ -203,9 +339,10 @@ public void setViewPager(ViewPager view) {
adapter.registerDataSetObserver(mObserver);
mViewPager = view;
- view.setOnPageChangeListener(this);
+ view.addOnPageChangeListener(this);
notifyDataSetChanged();
+ onPageSelected(mViewPager.getCurrentItem());
}
/**
@@ -240,8 +377,9 @@ public void onPageScrollStateChanged(int state) {
if(state == ViewPager.SCROLL_STATE_IDLE){
mScrolling = false;
TextView tv = getTabView(mSelectedPosition);
- if(tv != null)
- updateIndicator(tv.getLeft(), tv.getMeasuredWidth());
+ if(tv != null) {
+ updateIndicator(tv.getLeft(), tv.getMeasuredWidth());
+ }
}
else
mScrolling = true;
@@ -259,8 +397,8 @@ public void onPageScrolled(int position, float positionOffset, int positionOffse
CheckedTextView tv_next = getTabView(position + 1);
if(tv_scroll != null && tv_next != null){
- int width_scroll = tv_scroll.getWidth();
- int width_next = tv_next.getWidth();
+ int width_scroll = tv_scroll.getMeasuredWidth();
+ int width_next = tv_next.getMeasuredWidth();
float distance = (width_scroll + width_next) / 2f;
int width = (int)(width_scroll + (width_next - width_scroll) * positionOffset + 0.5f);
@@ -305,42 +443,39 @@ public void setCurrentItem(int position) {
}
private void notifyDataSetChanged() {
- mTabContainer.removeAllViews();
-
- PagerAdapter adapter = mViewPager.getAdapter();
- final int count = adapter.getCount();
-
- if (mSelectedPosition > count)
- mSelectedPosition = count - 1;
-
+ mTabContainer.removeAllViews();
+
+ PagerAdapter adapter = mViewPager.getAdapter();
+ final int count = adapter.getCount();
+
+ if (mSelectedPosition > count)
+ mSelectedPosition = count - 1;
+
for (int i = 0; i < count; i++) {
CharSequence title = adapter.getPageTitle(i);
- if (title == null)
- title = "NULL";
-
+ if (title == null)
+ title = "NULL";
+
CheckedTextView tv = new CheckedTextView(getContext());
tv.setCheckMarkDrawable(null);
tv.setText(title);
tv.setGravity(Gravity.CENTER);
tv.setTextAppearance(getContext(), mTextAppearance);
- tv.setSingleLine(true);
+ if(mTabSingleLine)
+ tv.setSingleLine(true);
+ else {
+ tv.setSingleLine(false);
+ tv.setMaxLines(2);
+ }
tv.setEllipsize(TruncateAt.END);
tv.setOnClickListener(this);
tv.setTag(i);
if(mTabRippleStyle > 0)
ViewUtil.setBackground(tv, new RippleDrawable.Builder(getContext(), mTabRippleStyle).build());
-
- if(mMode == MODE_SCROLL){
- tv.setPadding(mTabPadding, 0, mTabPadding, 0);
- mTabContainer.addView(tv, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT));
- }
- else if(mMode == MODE_FIXED){
- LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.MATCH_PARENT);
- params.weight = 1f;
- mTabContainer.addView(tv, params);
- }
-
- }
+
+ tv.setPadding(mTabPadding, 0, mTabPadding, 0);
+ mTabContainer.addView(tv, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT));
+ }
setCurrentItem(mSelectedPosition);
requestLayout();
@@ -392,4 +527,96 @@ else if(mMode == MODE_FIXED){
}
}
}
+
+ private class TabContainerLayout extends FrameLayout{
+
+ public TabContainerLayout(Context context) {
+ super(context);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ int widthMode = MeasureSpec.getMode(widthMeasureSpec);
+ int widthSize = MeasureSpec.getSize(widthMeasureSpec);
+
+ int width = 0;
+ int height = 0;
+
+ if(mMode == MODE_SCROLL){
+ int ws = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+ for (int i = 0; i < getChildCount(); i++) {
+ View child = getChildAt(i);
+ child.measure(ws, heightMeasureSpec);
+ width += child.getMeasuredWidth();
+ height = Math.max(height, child.getMeasuredHeight());
+ }
+ setMeasuredDimension(width, height);
+ }
+ else{
+ if(widthMode != MeasureSpec.EXACTLY){
+ int ws = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+ for (int i = 0; i < getChildCount(); i++) {
+ View child = getChildAt(i);
+ child.measure(ws, heightMeasureSpec);
+ width += child.getMeasuredWidth();
+ height = Math.max(height, child.getMeasuredHeight());
+ }
+
+ if(widthMode == MeasureSpec.UNSPECIFIED || width < widthSize)
+ setMeasuredDimension(widthSize, height);
+ else{
+ int childWidth = widthSize / getChildCount();
+ for (int i = 0, count = getChildCount(); i < count; i++) {
+ View child = getChildAt(i);
+ if(i != count - 1)
+ child.measure(MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY), heightMeasureSpec);
+ else
+ child.measure(MeasureSpec.makeMeasureSpec(widthSize - childWidth * (count - 1), MeasureSpec.EXACTLY), heightMeasureSpec);
+ }
+ setMeasuredDimension(widthSize, height);
+ }
+ }
+ else {
+ int childWidth = widthSize / getChildCount();
+ for (int i = 0, count = getChildCount(); i < count; i++) {
+ View child = getChildAt(i);
+ if(i != count - 1)
+ child.measure(MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY), heightMeasureSpec);
+ else
+ child.measure(MeasureSpec.makeMeasureSpec(widthSize - childWidth * (count - 1), MeasureSpec.EXACTLY), heightMeasureSpec);
+ height = Math.max(height, child.getMeasuredHeight());
+ }
+ setMeasuredDimension(widthSize, height);
+ }
+ }
+
+ int hs = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
+ for (int i = 0; i < getChildCount(); i++) {
+ View child = getChildAt(i);
+ if(child.getMeasuredHeight() != height)
+ child.measure(MeasureSpec.makeMeasureSpec(child.getMeasuredWidth(), MeasureSpec.EXACTLY), hs);
+ }
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ int childLeft = 0;
+ int childTop = 0;
+ int childRight = right - left;
+ int childBottom = bottom - top;
+
+ if(mIsRtl)
+ for(int i = 0, count = getChildCount(); i < count; i++){
+ View child = getChildAt(i);
+ child.layout(childRight - child.getMeasuredWidth(), childTop, childRight, childBottom);
+ childRight -= child.getMeasuredWidth();
+ }
+ else
+ for(int i = 0, count = getChildCount(); i < count; i++){
+ View child = getChildAt(i);
+ child.layout(childLeft, childTop, childLeft + child.getMeasuredWidth(), childBottom);
+ childLeft += child.getMeasuredWidth();
+ }
+ }
+ }
}
diff --git a/lib/src/main/java/com/rey/material/widget/TextView.java b/lib/src/main/java/com/rey/material/widget/TextView.java
index a9f99f9d..680056e1 100644
--- a/lib/src/main/java/com/rey/material/widget/TextView.java
+++ b/lib/src/main/java/com/rey/material/widget/TextView.java
@@ -7,11 +7,15 @@
import android.view.MotionEvent;
import android.view.View;
+import com.rey.material.app.ThemeManager;
import com.rey.material.drawable.RippleDrawable;
+import com.rey.material.util.ViewUtil;
-public class TextView extends android.widget.TextView {
+public class TextView extends android.widget.TextView implements ThemeManager.OnThemeChangedListener{
private RippleManager mRippleManager;
+ protected int mStyleId;
+ protected int mCurrentStyle = ThemeManager.THEME_UNDEFINED;
public interface OnSelectionChangedListener{
public void onSelectionChanged(View v, int selStart, int selEnd);
@@ -44,17 +48,46 @@ public TextView(Context context, AttributeSet attrs, int defStyleAttr, int defSt
}
private void init(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){
+ ViewUtil.applyFont(this, attrs, defStyleAttr, defStyleRes);
applyStyle(context, attrs, defStyleAttr, defStyleRes);
+ mStyleId = ThemeManager.getStyleId(context, attrs, defStyleAttr, defStyleRes);
}
public void applyStyle(int resId){
+ ViewUtil.applyStyle(this, resId);
applyStyle(getContext(), null, 0, resId);
}
- private void applyStyle(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){
+ protected void applyStyle(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){
getRippleManager().onCreate(this, context, attrs, defStyleAttr, defStyleRes);
}
+ @Override
+ public void onThemeChanged(ThemeManager.OnThemeChangedEvent event) {
+ int style = ThemeManager.getInstance().getCurrentStyle(mStyleId);
+ if(mCurrentStyle != style){
+ mCurrentStyle = style;
+ applyStyle(mCurrentStyle);
+ }
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ if(mStyleId != 0) {
+ ThemeManager.getInstance().registerOnThemeChangedListener(this);
+ onThemeChanged(null);
+ }
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ mRippleManager.cancelRipple(this);
+ if(mStyleId != 0)
+ ThemeManager.getInstance().unregisterOnThemeChangedListener(this);
+ }
+
@Override
public void setBackgroundDrawable(Drawable drawable) {
Drawable background = getBackground();
diff --git a/lib/src/main/java/com/rey/material/widget/TimePicker.java b/lib/src/main/java/com/rey/material/widget/TimePicker.java
index 82eaddf1..4c70e79e 100644
--- a/lib/src/main/java/com/rey/material/widget/TimePicker.java
+++ b/lib/src/main/java/com/rey/material/widget/TimePicker.java
@@ -20,6 +20,7 @@
import android.view.animation.Interpolator;
import com.rey.material.R;
+import com.rey.material.app.ThemeManager;
import com.rey.material.util.ColorUtil;
import com.rey.material.util.ThemeUtil;
import com.rey.material.util.TypefaceUtil;
@@ -28,19 +29,22 @@
/**
* Created by Rey on 12/19/2014.
*/
-public class TimePicker extends View{
+public class TimePicker extends View implements ThemeManager.OnThemeChangedListener{
+
+ protected int mStyleId;
+ protected int mCurrentStyle = ThemeManager.THEME_UNDEFINED;
private int mBackgroundColor;
private int mSelectionColor;
- private int mSelectionRadius;
- private int mTickSize;
- private Typeface mTypeface;
- private int mTextSize;
- private int mTextColor;
- private int mTextHighlightColor;
+ private int mSelectionRadius = -1;
+ private int mTickSize = -1;
+ private Typeface mTypeface = Typeface.DEFAULT;
+ private int mTextSize = -1;
+ private int mTextColor = 0xFF000000;
+ private int mTextHighlightColor = 0xFFFFFFFF;
private boolean m24Hour = true;
- private int mAnimDuration;
+ private int mAnimDuration = -1;
private Interpolator mInInterpolator;
private Interpolator mOutInterpolator;
private long mStartTime;
@@ -124,10 +128,16 @@ private void init(Context context, AttributeSet attrs, int defStyleAttr, int def
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mRect = new Rect();
+ mBackgroundColor = ColorUtil.getColor(ThemeUtil.colorPrimary(context, 0xFF000000), 0.25f);
+ mSelectionColor = ThemeUtil.colorPrimary(context, 0xFF000000);
+
+
initTickLabels();
setWillNotDraw(false);
applyStyle(context, attrs, defStyleAttr, defStyleRes);
+
+ mStyleId = ThemeManager.getStyleId(context, attrs, defStyleAttr, defStyleRes);
}
/**
@@ -148,37 +158,106 @@ private void initTickLabels(){
}
public void applyStyle(int styleId){
+ ViewUtil.applyStyle(this, styleId);
applyStyle(getContext(), null, 0, styleId);
}
- private void applyStyle(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){
+ protected void applyStyle(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TimePicker, defStyleAttr, defStyleRes);
- mBackgroundColor = a.getColor(R.styleable.TimePicker_tp_backgroundColor, ColorUtil.getColor(ThemeUtil.colorPrimary(context, 0xFF000000), 0.25f));
- mSelectionColor = a.getColor(R.styleable.TimePicker_tp_selectionColor, ThemeUtil.colorPrimary(context, 0xFF000000));
- mSelectionRadius = a.getDimensionPixelOffset(R.styleable.TimePicker_tp_selectionRadius, ThemeUtil.dpToPx(context, 8));
- mTickSize = a.getDimensionPixelSize(R.styleable.TimePicker_tp_tickSize, ThemeUtil.dpToPx(context, 1));
- mTextSize = a.getDimensionPixelSize(R.styleable.TimePicker_tp_textSize, context.getResources().getDimensionPixelOffset(R.dimen.abc_text_size_caption_material));
- mTextColor = a.getColor(R.styleable.TimePicker_tp_textColor, 0xFF000000);
- mTextHighlightColor = a.getColor(R.styleable.TimePicker_tp_textHighlightColor, 0xFFFFFFFF);
- mAnimDuration = a.getInteger(R.styleable.TimePicker_tp_animDuration, context.getResources().getInteger(android.R.integer.config_mediumAnimTime));
- int resId = a.getResourceId(R.styleable.TimePicker_tp_inInterpolator, 0);
- mInInterpolator = resId == 0 ? new DecelerateInterpolator() : AnimationUtils.loadInterpolator(context, resId);
- resId = a.getResourceId(R.styleable.TimePicker_tp_outInterpolator, 0);
- mOutInterpolator = resId == 0 ? new DecelerateInterpolator() : AnimationUtils.loadInterpolator(context, resId);
- setMode(a.getInteger(R.styleable.TimePicker_tp_mode, mMode), false);
- if(a.hasValue(R.styleable.TimePicker_tp_24Hour))
- set24Hour(a.getBoolean(R.styleable.TimePicker_tp_24Hour, m24Hour));
- else
+
+ boolean hourDefined = false;
+ String familyName = null;
+ int style = -1;
+
+ for(int i = 0, count = a.getIndexCount(); i < count; i++){
+ int attr = a.getIndex(i);
+
+ if(attr == R.styleable.TimePicker_tp_backgroundColor)
+ mBackgroundColor = a.getColor(attr, 0);
+ else if(attr == R.styleable.TimePicker_tp_selectionColor)
+ mSelectionColor = a.getColor(attr, 0);
+ else if(attr == R.styleable.TimePicker_tp_selectionRadius)
+ mSelectionRadius = a.getDimensionPixelOffset(attr, 0);
+ else if(attr == R.styleable.TimePicker_tp_tickSize)
+ mTickSize = a.getDimensionPixelOffset(attr, 0);
+ else if(attr == R.styleable.TimePicker_tp_textSize)
+ mTextSize = a.getDimensionPixelOffset(attr, 0);
+ else if(attr == R.styleable.TimePicker_tp_textColor)
+ mTextColor = a.getColor(attr, 0);
+ else if(attr == R.styleable.TimePicker_tp_textHighlightColor)
+ mTextHighlightColor = a.getColor(attr, 0);
+ else if(attr == R.styleable.TimePicker_tp_animDuration)
+ mAnimDuration = a.getInteger(attr, 0);
+ else if(attr == R.styleable.TimePicker_tp_inInterpolator)
+ mInInterpolator = AnimationUtils.loadInterpolator(context, a.getResourceId(attr, 0));
+ else if(attr == R.styleable.TimePicker_tp_outInterpolator)
+ mOutInterpolator = AnimationUtils.loadInterpolator(context, a.getResourceId(attr, 0));
+ else if(attr == R.styleable.TimePicker_tp_mode)
+ setMode(a.getInteger(attr, 0), false);
+ else if(attr == R.styleable.TimePicker_tp_24Hour) {
+ set24Hour(a.getBoolean(attr, false));
+ hourDefined = true;
+ }
+ else if(attr == R.styleable.TimePicker_tp_hour)
+ setHour(a.getInteger(attr, 0));
+ else if(attr == R.styleable.TimePicker_tp_minute)
+ setMinute(a.getInteger(attr, 0));
+ else if(attr == R.styleable.TimePicker_tp_fontFamily)
+ familyName = a.getString(attr);
+ else if(attr == R.styleable.TimePicker_tp_textStyle)
+ style = a.getInteger(attr, 0);
+ }
+
+ a.recycle();
+
+ if(mSelectionRadius < 0)
+ mSecondInnerRadius = ThemeUtil.dpToPx(context, 8);
+
+ if(mTickSize < 0)
+ mTickSize = ThemeUtil.dpToPx(context, 1);
+
+ if(mTextSize < 0)
+ mTextSize = context.getResources().getDimensionPixelOffset(R.dimen.abc_text_size_caption_material);
+
+ if(mAnimDuration < 0)
+ mAnimDuration = context.getResources().getInteger(android.R.integer.config_mediumAnimTime);
+
+ if(mInInterpolator == null)
+ mInInterpolator = new DecelerateInterpolator();
+
+ if(mOutInterpolator == null)
+ mOutInterpolator = new DecelerateInterpolator();
+
+ if(!hourDefined)
set24Hour(DateFormat.is24HourFormat(context));
- setHour(a.getInteger(R.styleable.TimePicker_tp_hour, mHour));
- setMinute(a.getInteger(R.styleable.TimePicker_tp_minute, mMinute));
- String familyName = a.getString(R.styleable.TimePicker_tp_fontFamily);
- int style = a.getInteger(R.styleable.TimePicker_tp_textStyle, Typeface.NORMAL);
+ if(familyName != null || style >= 0)
+ mTypeface = TypefaceUtil.load(context, familyName, style);
+ }
- mTypeface = TypefaceUtil.load(context, familyName, style);
+ @Override
+ public void onThemeChanged(ThemeManager.OnThemeChangedEvent event) {
+ int style = ThemeManager.getInstance().getCurrentStyle(mStyleId);
+ if(mCurrentStyle != style){
+ mCurrentStyle = style;
+ applyStyle(mCurrentStyle);
+ }
+ }
- a.recycle();
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ if(mStyleId != 0) {
+ ThemeManager.getInstance().registerOnThemeChangedListener(this);
+ onThemeChanged(null);
+ }
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ if(mStyleId != 0)
+ ThemeManager.getInstance().unregisterOnThemeChangedListener(this);
}
public int getBackgroundColor(){
diff --git a/lib/src/main/java/com/rey/material/widget/YearPicker.java b/lib/src/main/java/com/rey/material/widget/YearPicker.java
index 1531a18c..d9cab160 100644
--- a/lib/src/main/java/com/rey/material/widget/YearPicker.java
+++ b/lib/src/main/java/com/rey/material/widget/YearPicker.java
@@ -21,26 +21,28 @@
import android.widget.BaseAdapter;
import com.rey.material.R;
+import com.rey.material.app.ThemeManager;
import com.rey.material.drawable.BlankDrawable;
import com.rey.material.util.ThemeUtil;
import com.rey.material.util.TypefaceUtil;
+import com.rey.material.util.ViewUtil;
import java.util.Calendar;
/**
* Created by Rey on 12/26/2014.
*/
-public class YearPicker extends ListView{
+public class YearPicker extends ListView {
private YearAdapter mAdapter;
- private int mTextSize;
- private int mItemHeight;
+ private int mTextSize = -1;
+ private int mItemHeight = -1;
private int mSelectionColor;
- private int mAnimDuration;
+ private int mAnimDuration = -1;
private Interpolator mInInterpolator;
private Interpolator mOutInterpolator;
- private Typeface mTypeface;
+ private Typeface mTypeface = Typeface.DEFAULT;
private int mItemRealHeight = -1;
private int mPadding;
@@ -70,7 +72,7 @@ public interface OnYearChangedListener{
new int[]{android.R.attr.state_checked},
};
- private int[] mTextColors = new int[2];
+ private int[] mTextColors = new int[]{0xFF000000, 0xFFFFFFFF};
private static final String YEAR_FORMAT = "%4d";
@@ -113,54 +115,99 @@ private void init(Context context, AttributeSet attrs, int defStyleAttr, int def
mPadding = ThemeUtil.dpToPx(context, 4);
+ mSelectionColor = ThemeUtil.colorPrimary(context, 0xFF000000);
+
applyStyle(context, attrs, defStyleAttr, defStyleRes);
}
- public void applyStyle(int resId){
- applyStyle(getContext(), null, 0, resId);
- }
+ @Override
+ protected void applyStyle(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){
+ super.applyStyle(context, attrs, defStyleAttr, defStyleRes);
- private void applyStyle(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.YearPicker, defStyleAttr, defStyleRes);
- mTextSize = a.getDimensionPixelSize(R.styleable.YearPicker_dp_yearTextSize, context.getResources().getDimensionPixelOffset(R.dimen.abc_text_size_title_material));
- int year = a.getInteger(R.styleable.YearPicker_dp_year, mAdapter.getYear());
- int yearMin = a.getInteger(R.styleable.YearPicker_dp_yearMin, mAdapter.getMinYear());
- int yearMax = a.getInteger(R.styleable.YearPicker_dp_yearMax, mAdapter.getMaxYear());
- mItemHeight = a.getDimensionPixelSize(R.styleable.YearPicker_dp_yearItemHeight, ThemeUtil.dpToPx(context, 48));
- mTextColors[0] = a.getColor(R.styleable.YearPicker_dp_textColor, 0xFF000000);
- mTextColors[1] = a.getColor(R.styleable.YearPicker_dp_textHighlightColor, 0xFFFFFFFF);
- mSelectionColor = a.getColor(R.styleable.YearPicker_dp_selectionColor, ThemeUtil.colorPrimary(context, 0xFF000000));
- mAnimDuration = a.getInteger(R.styleable.YearPicker_dp_animDuration, context.getResources().getInteger(android.R.integer.config_mediumAnimTime));
- int resId = a.getResourceId(R.styleable.YearPicker_dp_inInterpolator, 0);
- if(resId != 0)
- mInInterpolator = AnimationUtils.loadInterpolator(context, resId);
- else
+
+ int year = -1;
+ int yearMin = -1;
+ int yearMax = -1;
+ String familyName = null;
+ int style = -1;
+
+ for(int i = 0, count = a.getIndexCount(); i < count; i++){
+ int attr = a.getIndex(i);
+
+ if(attr == R.styleable.YearPicker_dp_yearTextSize)
+ mTextSize = a.getDimensionPixelSize(attr, 0);
+ else if(attr == R.styleable.YearPicker_dp_year)
+ year = a.getInteger(attr, 0);
+ else if(attr == R.styleable.YearPicker_dp_yearMin)
+ yearMin = a.getInteger(attr, 0);
+ else if(attr == R.styleable.YearPicker_dp_yearMax)
+ yearMax = a.getInteger(attr, 0);
+ else if(attr == R.styleable.YearPicker_dp_yearItemHeight)
+ mItemHeight = a.getDimensionPixelSize(attr, 0);
+ else if(attr == R.styleable.YearPicker_dp_textColor)
+ mTextColors[0] = a.getColor(attr, 0);
+ else if(attr == R.styleable.YearPicker_dp_textHighlightColor)
+ mTextColors[1] = a.getColor(attr, 0);
+ else if(attr == R.styleable.YearPicker_dp_selectionColor)
+ mSelectionColor = a.getColor(attr, 0);
+ else if(attr == R.styleable.YearPicker_dp_animDuration)
+ mAnimDuration = a.getInteger(attr, 0);
+ else if(attr == R.styleable.YearPicker_dp_inInterpolator)
+ mInInterpolator = AnimationUtils.loadInterpolator(context, a.getResourceId(attr, 0));
+ else if(attr == R.styleable.YearPicker_dp_outInterpolator)
+ mOutInterpolator = AnimationUtils.loadInterpolator(context, a.getResourceId(attr, 0));
+ else if(attr == R.styleable.YearPicker_dp_fontFamily)
+ familyName = a.getString(attr);
+ else if(attr == R.styleable.YearPicker_dp_textStyle)
+ style = a.getInteger(attr, 0);
+ }
+
+ a.recycle();
+
+ if(mTextSize < 0)
+ mTextSize = context.getResources().getDimensionPixelOffset(R.dimen.abc_text_size_title_material);
+
+ if(mItemHeight < 0)
+ mItemHeight = ThemeUtil.dpToPx(context, 48);
+
+ if(mAnimDuration < 0)
+ mAnimDuration = context.getResources().getInteger(android.R.integer.config_mediumAnimTime);
+
+ if(mInInterpolator == null)
mInInterpolator = new DecelerateInterpolator();
- resId = a.getResourceId(R.styleable.YearPicker_dp_outInterpolator, 0);
- if(resId != 0)
- mOutInterpolator = AnimationUtils.loadInterpolator(context, resId);
- else
+
+ if(mOutInterpolator == null)
mOutInterpolator = new DecelerateInterpolator();
- String familyName = a.getString(R.styleable.YearPicker_dp_fontFamily);
- int style = a.getInteger(R.styleable.YearPicker_dp_textStyle, Typeface.NORMAL);
- mTypeface = TypefaceUtil.load(context, familyName, style);
+ if(familyName != null || style >= 0)
+ mTypeface = TypefaceUtil.load(context, familyName, style);
- a.recycle();
+ if(yearMin >= 0 || yearMax >= 0){
+ if(yearMin < 0)
+ yearMin = mAdapter.getMinYear();
- if(yearMax < yearMin)
- yearMax = Integer.MAX_VALUE;
+ if(yearMax < 0)
+ yearMax = mAdapter.getMaxYear();
- if(year < 0){
+ if(yearMax < yearMin)
+ yearMax = Integer.MAX_VALUE;
+
+ setYearRange(yearMin, yearMax);
+ }
+
+ if(mAdapter.getYear() < 0 && year < 0){
Calendar cal = Calendar.getInstance();
year = cal.get(Calendar.YEAR);
}
- year = Math.max(yearMin, Math.min(yearMax, year));
+ if(year >= 0){
+ year = Math.max(yearMin, Math.min(yearMax, year));
+ setYear(year);
+ }
- setYearRange(yearMin, yearMax);
- setYear(year);
mAdapter.notifyDataSetChanged();
+ requestLayout();
}
/**
diff --git a/lib/src/main/res/values/attrs.xml b/lib/src/main/res/values/attrs.xml
index 085a6886..61b28991 100644
--- a/lib/src/main/res/values/attrs.xml
+++ b/lib/src/main/res/values/attrs.xml
@@ -13,6 +13,122 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -207,6 +323,7 @@
+
@@ -221,6 +338,7 @@
+
@@ -324,6 +442,7 @@
+
@@ -378,6 +497,8 @@
+
+
@@ -525,4 +646,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lib/src/main/res/values/styles.xml b/lib/src/main/res/values/styles.xml
index 904faf49..dbedb323 100644
--- a/lib/src/main/res/values/styles.xml
+++ b/lib/src/main/res/values/styles.xml
@@ -1,5 +1,10 @@
+
+
@@ -212,10 +217,10 @@
- 0
- 100
- 1
- - 0
- false
- @dimen/abc_text_size_small_material
- #FFFFFFFF
+ - false
@@ -533,7 +539,6 @@
- @color/abc_secondary_text_material_dark
- ?attr/colorAccent
- end
- - none
+
+