diff --git a/library/src/main/java/lumenghz/com/pullrefresh/PullToRefreshView.java b/library/src/main/java/lumenghz/com/pullrefresh/PullToRefreshView.java index a6d9c8e..d380085 100644 --- a/library/src/main/java/lumenghz/com/pullrefresh/PullToRefreshView.java +++ b/library/src/main/java/lumenghz/com/pullrefresh/PullToRefreshView.java @@ -22,6 +22,7 @@ import lumenghz.com.pullrefresh.refresh_view.BaseRefreshView; import lumenghz.com.pullrefresh.refresh_view.RocketRefreshView; +import lumenghz.com.pullrefresh.refresh_view.SunRefreshView; import lumenghz.com.pullrefresh.util.Utils; /** @@ -30,10 +31,12 @@ */ public class PullToRefreshView extends ViewGroup { private static final int DRAG_MAX_DISTANCE = 120; + private static final int DRAW_MAX_DISTANCE_SUN = 140; private static final float DRAG_RATE = .5f; private static final float DECELERATE_INTERPOLATION_FACTOR = 2f; - public static final int STYPE_ROCKET = 0; + public static final int TYPE_ROCKET = 0; + public static final int TYPE_SUN = 1; public static final int MAX_OFFSET_ANIMATION_DURATION = 700; private static final int INVALID_POINTER = -1; @@ -78,27 +81,38 @@ public PullToRefreshView(Context context) { public PullToRefreshView(Context context, AttributeSet attrs) { super(context, attrs); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PullToRefreshView); - final int type = a.getInteger(R.styleable.PullToRefreshView_lrefresh, STYPE_ROCKET); + final int type = a.getInteger(R.styleable.PullToRefreshView_lrefresh, TYPE_ROCKET); a.recycle(); mDecelerateInterpolator = new DecelerateInterpolator(DECELERATE_INTERPOLATION_FACTOR); - mTotalDragDistance = Utils.convertDpToPixel(context, DRAG_MAX_DISTANCE); mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); mRefreshView = new ImageView(context); - setRefreshStyle(type); + setRefreshStyle(context, type); addView(mRefreshView); setWillNotDraw(false); ViewCompat.setChildrenDrawingOrderEnabled(this, true); } - private void setRefreshStyle(int type) { + /** + * Set landscape's height dynamically because different landscape suitable different heights. + * + * @param context context + * @param type type of theme which chosen by user + * @see {@link #mTotalDragDistance = Utils.convertDpToPixel} in this method + */ + private void setRefreshStyle(Context context, int type) { setRefreshing(false); switch (type) { - case STYPE_ROCKET: + case TYPE_ROCKET: mBaseRefreshView = new RocketRefreshView(getContext(), this); + mTotalDragDistance = Utils.convertDpToPixel(context, DRAG_MAX_DISTANCE); + break; + case TYPE_SUN: + mBaseRefreshView = new SunRefreshView(getContext(), this); + mTotalDragDistance = Utils.convertDpToPixel(context, DRAW_MAX_DISTANCE_SUN); break; default: throw new InvalidParameterException("Type is not exists"); diff --git a/library/src/main/java/lumenghz/com/pullrefresh/refresh_view/AnimationFractory.java b/library/src/main/java/lumenghz/com/pullrefresh/refresh_view/AnimationFactory.java similarity index 75% rename from library/src/main/java/lumenghz/com/pullrefresh/refresh_view/AnimationFractory.java rename to library/src/main/java/lumenghz/com/pullrefresh/refresh_view/AnimationFactory.java index 811017b..4123f74 100644 --- a/library/src/main/java/lumenghz/com/pullrefresh/refresh_view/AnimationFractory.java +++ b/library/src/main/java/lumenghz/com/pullrefresh/refresh_view/AnimationFactory.java @@ -4,17 +4,20 @@ import android.view.animation.AccelerateInterpolator; import android.view.animation.Animation; import android.view.animation.Interpolator; +import android.view.animation.LinearInterpolator; /** * @author lumeng on 2016-06-16. * jiahehz@gmail.com */ -public class AnimationFractory { +public class AnimationFactory { private static final int ANIMATION_FIRE_BURN_DURATION = 180; private static final int ANIMATION_FIRE_SCALE_DURATION = 100; + private static final int ANIMATION_SUN_ROTATE_DURATION = 1000; private static final Interpolator ACCELERATE_INTERPOLATOR = new AccelerateInterpolator(); private static final Interpolator DECELERATE_INTERPOLATOR = new AccelerateDecelerateInterpolator(); + private static final Interpolator NORMAL_INTERPOLATOR = new LinearInterpolator(); Animation getFireScale(Animation animation) { configureAnimation(animation, @@ -38,6 +41,17 @@ Animation getFireBurn(Animation animation) { return animation; } + Animation getSunRotate(Animation animation) { + configureAnimation(animation, + NORMAL_INTERPOLATOR, + ANIMATION_SUN_ROTATE_DURATION, + 0, + Animation.RESTART, + Animation.INFINITE); + + return animation; + } + private void configureAnimation(Animation animation, Interpolator interpolator, int duration, int startOffset, int repeatMode, int repeatCount) { animation.setInterpolator(interpolator); animation.setDuration(duration); diff --git a/library/src/main/java/lumenghz/com/pullrefresh/refresh_view/BaseRefreshView.java b/library/src/main/java/lumenghz/com/pullrefresh/refresh_view/BaseRefreshView.java index 2cd434a..a59b89e 100644 --- a/library/src/main/java/lumenghz/com/pullrefresh/refresh_view/BaseRefreshView.java +++ b/library/src/main/java/lumenghz/com/pullrefresh/refresh_view/BaseRefreshView.java @@ -10,6 +10,7 @@ import android.os.Build; import lumenghz.com.pullrefresh.PullToRefreshView; +import lumenghz.com.pullrefresh.util.Utils; /** * @author lumeng on 2016-06-16. @@ -31,6 +32,14 @@ protected Context getContext() { public abstract void offsetTopAndBottom(int offset); + protected abstract void initialDimens(int viewWidth); + + protected abstract void setupAnimations(); + + protected int getPixel(int dp) { + return Utils.convertDpToPixel(getContext(), dp); + } + @TargetApi(Build.VERSION_CODES.HONEYCOMB) @Override public void invalidateDrawable(Drawable who) { diff --git a/library/src/main/java/lumenghz/com/pullrefresh/refresh_view/RocketRefreshView.java b/library/src/main/java/lumenghz/com/pullrefresh/refresh_view/RocketRefreshView.java index 2a84345..1cb5095 100644 --- a/library/src/main/java/lumenghz/com/pullrefresh/refresh_view/RocketRefreshView.java +++ b/library/src/main/java/lumenghz/com/pullrefresh/refresh_view/RocketRefreshView.java @@ -83,7 +83,8 @@ public void run() { }); } - private void initialDimens(int viewWidth) { + @Override + protected void initialDimens(int viewWidth) { if (viewWidth <= 0 || viewWidth == mScreenWidth) return; mScreenWidth = viewWidth; @@ -267,8 +268,9 @@ public boolean isRunning() { return false; } - private void setupAnimations() { - AnimationFractory animationFractory = new AnimationFractory(); + @Override + protected void setupAnimations() { + AnimationFactory animationFractory = new AnimationFactory(); mFireBurnAnimation = animationFractory.getFireBurn(new Animation() { @Override protected void applyTransformation(float interpolatedTime, Transformation t) { diff --git a/library/src/main/java/lumenghz/com/pullrefresh/refresh_view/SunRefreshView.java b/library/src/main/java/lumenghz/com/pullrefresh/refresh_view/SunRefreshView.java new file mode 100644 index 0000000..e6e7ad8 --- /dev/null +++ b/library/src/main/java/lumenghz/com/pullrefresh/refresh_view/SunRefreshView.java @@ -0,0 +1,257 @@ +package lumenghz.com.pullrefresh.refresh_view; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.util.Log; +import android.view.animation.Animation; +import android.view.animation.Transformation; + +import lumenghz.com.pullrefresh.PullToRefreshView; +import lumenghz.com.pullrefresh.R; + +/** + * @author lumeng on 2016-07-01. + */ +public class SunRefreshView extends BaseRefreshView { + private static final float HEIGHT_RATIO = 1.0f; + private static final float BUILDING_INITIAL_SCALE = 1.0f; + private static final float BUILDING_FINAL_SCALE = 1.2f; + + private PullToRefreshView mParent; + + private Matrix mMatrix; + + private Context mContext; + + private Animation mSunAnimation; + + private Bitmap mSun; + private Bitmap mBuilding; + + private Paint mBackgroundPaint; + + private boolean isRefreshing = false; + private boolean isSunRise = true; + + private float mBuildingTopOffset; + + private float mSunRotateAngle; + private float mPercent; + + private int mSunWidth; + private int mSunHeight; + + /** + * height of landscape + */ + private int mSenceHeight; + /** + * width of landscape + */ + private int mScreenWidth; + /** + * distance between bottom of landscape and top of landscape + */ + private int mTop; + /** + * max distance between bottom of landscape and top of landscape + */ + private int totalDistance; + + public SunRefreshView(Context context, final PullToRefreshView layout) { + super(context, layout); + + mParent = layout; + mMatrix = new Matrix(); + mContext = getContext(); + setupAnimations(); + setupPaint(); + layout.post(new Runnable() { + @Override + public void run() { + initialDimens(layout.getWidth()); + } + }); + } + + @Override + protected void initialDimens(int viewWidth) { + if (viewWidth <= 0 || viewWidth == mScreenWidth) return; + + createBitmaps(); + + mScreenWidth = viewWidth; + mSenceHeight = (int) (HEIGHT_RATIO * mScreenWidth); + + mTop = -mParent.getTotalDragDistance(); + totalDistance = -mTop; + + mBuildingTopOffset = -mTop - mBuilding.getHeight(); + + mSunWidth = mSun.getWidth(); + mSunHeight = mSun.getHeight(); + } + + private void createBitmaps() { + mSun = CreateBitmapFactory.getBitmapFromImage(R.drawable.sun, mContext); + mBuilding = CreateBitmapFactory.getBitmapFromImage(R.drawable.home_title_building_hz, mContext); + } + + @Override + public void draw(Canvas canvas) { + if (mScreenWidth <= 0) return; + + final int saveCount = canvas.save(); + + canvas.translate(0, mTop); + canvas.clipRect(0, -mTop, mScreenWidth, mParent.getTotalDragDistance()); + canvas.drawRect(0, -mTop, mScreenWidth, mParent.getTotalDragDistance(), mBackgroundPaint); + + drawBuilding(canvas); + drawSun(canvas); + + canvas.restoreToCount(saveCount); + } + + /** + * Draw building + * + * @param canvas canvas + */ + private void drawBuilding(Canvas canvas) { + final Matrix matrix = mMatrix; + matrix.reset(); + + float dragPercent = Math.min(1f, Math.abs(mPercent)); + + float buildingScale; + + buildingScale = BUILDING_INITIAL_SCALE + (BUILDING_FINAL_SCALE - BUILDING_INITIAL_SCALE) * dragPercent; + matrix.preScale(buildingScale, buildingScale); + + final float offsetX = mScreenWidth / 2 + - mBuilding.getWidth() / 2 + + (1f - buildingScale) * mBuilding.getWidth() / 2; + final float offsetY = mBuildingTopOffset; + + matrix.postTranslate(offsetX, offsetY); + canvas.drawBitmap(mBuilding, matrix, null); + } + + /** + * Draw sun + * + * @param canvas canvas + */ + private void drawSun(Canvas canvas) { + final Matrix matrix = mMatrix; + matrix.reset(); + + float dragPercent = Math.min(1f, Math.abs(mPercent)); + + final float offsetX = isRefreshing ? mScreenWidth / 2 * (2 - dragPercent) - mSunWidth / 2 : (mScreenWidth * dragPercent - mSunWidth) / 2; + final float offsetY = totalDistance * func(dragPercent); + + matrix.preRotate(mSunRotateAngle == 0.0f ? (360 * dragPercent) : mSunRotateAngle, mSunWidth / 2, mSunHeight / 2); + matrix.postTranslate(offsetX, offsetY); + canvas.drawBitmap(mSun, matrix, null); + } + + @Override + protected void setupAnimations() { + AnimationFactory factory = new AnimationFactory(); + mSunAnimation = factory.getSunRotate(new Animation() { + @Override + protected void applyTransformation(float interpolatedTime, Transformation t) { + mSunRotateAngle = 720 * setVariable(interpolatedTime); + } + }); + mSunAnimation.setAnimationListener(new Animation.AnimationListener() { + @Override + public void onAnimationStart(Animation animation) { + } + + @Override + public void onAnimationEnd(Animation animation) { + resetOrigins(); + } + + @Override + public void onAnimationRepeat(Animation animation) { + } + }); + } + + private void setupPaint() { + mBackgroundPaint = new Paint(); + mBackgroundPaint.setColor(Color.rgb(251, 66, 49)); + mBackgroundPaint.setStyle(Paint.Style.FILL); + } + + @Override + public void setBounds(int left, int top, int right, int bottom) { + super.setBounds(left, top, right, mSenceHeight + top); + } + + @Override + public void setPercent(float percent, boolean invalidate) { + setPercent(percent); + } + + private void setPercent(float percent) { + this.mPercent = percent; + } + + @Override + public void offsetTopAndBottom(int offset) { + mTop += offset; + invalidateSelf(); + } + + @Override + public void start() { + isRefreshing = true; + mSunAnimation.reset(); + + mParent.startAnimation(mSunAnimation); + } + + @Override + public void stop() { + mParent.clearAnimation(); + isRefreshing = false; + } + + @Override + public boolean isRunning() { + return false; + } + + private float setVariable(float value) { + invalidateSelf(); + return value; + } + + private void resetOrigins() { + setPercent(0); + mSunRotateAngle = 0.0f; + isSunRise = true; + } + + /** + * This is sun's moving-equation + * (y-3.1)² (x-1.8)² + * -------- + -------- = 1 + * 3.3*3.3 2*2 + * + * @param degree x value in the equation + * @return y value int the equation + */ + public float func(float degree) { + return -(float) Math.sqrt((1.00 - Math.pow(degree - 2.32, 2) / 7.29) * 5.9049) + 2.20f; + } +} diff --git a/library/src/main/res/drawable-xhdpi/home_title_building_cq.png b/library/src/main/res/drawable-xhdpi/home_title_building_cq.png new file mode 100755 index 0000000..f548dda Binary files /dev/null and b/library/src/main/res/drawable-xhdpi/home_title_building_cq.png differ diff --git a/library/src/main/res/drawable-xhdpi/home_title_building_cs.png b/library/src/main/res/drawable-xhdpi/home_title_building_cs.png new file mode 100755 index 0000000..34d8617 Binary files /dev/null and b/library/src/main/res/drawable-xhdpi/home_title_building_cs.png differ diff --git a/library/src/main/res/drawable-xhdpi/home_title_building_default.png b/library/src/main/res/drawable-xhdpi/home_title_building_default.png new file mode 100755 index 0000000..641e9dd Binary files /dev/null and b/library/src/main/res/drawable-xhdpi/home_title_building_default.png differ diff --git a/library/src/main/res/drawable-xhdpi/home_title_building_fz.png b/library/src/main/res/drawable-xhdpi/home_title_building_fz.png new file mode 100755 index 0000000..244f3b9 Binary files /dev/null and b/library/src/main/res/drawable-xhdpi/home_title_building_fz.png differ diff --git a/library/src/main/res/drawable-xhdpi/home_title_building_hz.png b/library/src/main/res/drawable-xhdpi/home_title_building_hz.png new file mode 100755 index 0000000..29ac77a Binary files /dev/null and b/library/src/main/res/drawable-xhdpi/home_title_building_hz.png differ diff --git a/library/src/main/res/drawable-xhdpi/home_title_refresh_text15.png b/library/src/main/res/drawable-xhdpi/home_title_refresh_text15.png new file mode 100755 index 0000000..029c225 Binary files /dev/null and b/library/src/main/res/drawable-xhdpi/home_title_refresh_text15.png differ diff --git a/library/src/main/res/drawable-xhdpi/sun.png b/library/src/main/res/drawable-xhdpi/sun.png new file mode 100755 index 0000000..381733d Binary files /dev/null and b/library/src/main/res/drawable-xhdpi/sun.png differ diff --git a/library/src/main/res/drawable-xhdpi/weather_hori_line.png b/library/src/main/res/drawable-xhdpi/weather_hori_line.png new file mode 100755 index 0000000..4946814 Binary files /dev/null and b/library/src/main/res/drawable-xhdpi/weather_hori_line.png differ diff --git a/library/src/main/res/values/attrs.xml b/library/src/main/res/values/attrs.xml index 935b076..a71c917 100644 --- a/library/src/main/res/values/attrs.xml +++ b/library/src/main/res/values/attrs.xml @@ -3,6 +3,7 @@ + \ No newline at end of file diff --git a/sample/src/main/res/layout/fragment_listview.xml b/sample/src/main/res/layout/fragment_listview.xml index c72ac00..0f6032e 100644 --- a/sample/src/main/res/layout/fragment_listview.xml +++ b/sample/src/main/res/layout/fragment_listview.xml @@ -10,7 +10,7 @@ android:id="@+id/pull_to_refresh" android:layout_width="match_parent" android:layout_height="match_parent" - app:lrefresh="rocket" + app:lrefresh="sun" > + android:layout_width="100dp" + android:layout_height="100dp" /> \ No newline at end of file diff --git a/sample/src/main/res/mipmap-hdpi/ic_launcher.png b/sample/src/main/res/mipmap-hdpi/ic_launcher.png index 2a1b0a7..d20b458 100644 Binary files a/sample/src/main/res/mipmap-hdpi/ic_launcher.png and b/sample/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/sample/src/main/res/mipmap-mdpi/ic_launcher.png b/sample/src/main/res/mipmap-mdpi/ic_launcher.png index bf97b7b..c8b8311 100644 Binary files a/sample/src/main/res/mipmap-mdpi/ic_launcher.png and b/sample/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/sample/src/main/res/mipmap-xhdpi/ic_launcher.png b/sample/src/main/res/mipmap-xhdpi/ic_launcher.png index 9b1f833..cc73b3d 100644 Binary files a/sample/src/main/res/mipmap-xhdpi/ic_launcher.png and b/sample/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/sample/src/main/res/mipmap-xxhdpi/ic_launcher.png b/sample/src/main/res/mipmap-xxhdpi/ic_launcher.png index ccbbe74..5bac4dd 100644 Binary files a/sample/src/main/res/mipmap-xxhdpi/ic_launcher.png and b/sample/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png index 65900ce..67f4141 100644 Binary files a/sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png and b/sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/sample/src/main/res/values/colors.xml b/sample/src/main/res/values/colors.xml index e17005e..1b6235b 100644 --- a/sample/src/main/res/values/colors.xml +++ b/sample/src/main/res/values/colors.xml @@ -2,7 +2,7 @@ #2B2E4A #2B2E4A - #E84545 + #e1e0e0 #6fa7d7 #ca703f