From 759744984e3fdc92511aa3d7fdb56a2e1b356d05 Mon Sep 17 00:00:00 2001 From: Floens Date: Tue, 26 Dec 2017 21:30:45 +0100 Subject: [PATCH] replace last usages of layout animations fix threadlistlayout recyclerview padding mess animate search like reply with a translation animation dont show comment counter when we dont know the board max comment count remove some todo's --- .../floens/chan/core/model/orm/Filter.java | 1 - .../chan/core/presenter/ReplyPresenter.java | 3 + .../chan/ui/animation/AnimationUtils.java | 157 +----------------- .../ui/controller/MainSettingsController.java | 5 +- .../ui/controller/PassSettingsController.java | 23 ++- .../floens/chan/ui/layout/ReplyLayout.java | 7 +- .../chan/ui/layout/ThreadListLayout.java | 86 +++++++--- .../chan/ui/settings/SettingsController.java | 22 +-- .../src/main/res/layout/controller_pass.xml | 4 +- .../main/res/layout/layout_thread_list.xml | 2 +- 10 files changed, 98 insertions(+), 212 deletions(-) diff --git a/Clover/app/src/main/java/org/floens/chan/core/model/orm/Filter.java b/Clover/app/src/main/java/org/floens/chan/core/model/orm/Filter.java index d7ffd3ee..25e0b112 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/model/orm/Filter.java +++ b/Clover/app/src/main/java/org/floens/chan/core/model/orm/Filter.java @@ -40,7 +40,6 @@ public class Filter { @DatabaseField(canBeNull = false) public boolean allBoards = true; - // TODO(multi-site) @DatabaseField(canBeNull = false) public String boards; diff --git a/Clover/app/src/main/java/org/floens/chan/core/presenter/ReplyPresenter.java b/Clover/app/src/main/java/org/floens/chan/core/presenter/ReplyPresenter.java index b8c82f96..c1add0d2 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/presenter/ReplyPresenter.java +++ b/Clover/app/src/main/java/org/floens/chan/core/presenter/ReplyPresenter.java @@ -111,6 +111,7 @@ public class ReplyPresenter implements CaptchaCallback, ImagePickDelegate.ImageP callback.loadDraftIntoViews(draft); callback.updateCommentCount(0, board.maxCommentChars, false); callback.setCommentHint(getString(loadable.isThreadMode() ? R.string.reply_comment_thread : R.string.reply_comment_board)); + callback.showCommentCounter(board.maxCommentChars > 0); if (draft.file != null) { showPreview(draft.fileName, draft.file); @@ -453,6 +454,8 @@ public class ReplyPresenter implements CaptchaCallback, ImagePickDelegate.ImageP void setCommentHint(String hint); + void showCommentCounter(boolean show); + void setExpanded(boolean expanded); void openNameOptions(boolean open); diff --git a/Clover/app/src/main/java/org/floens/chan/ui/animation/AnimationUtils.java b/Clover/app/src/main/java/org/floens/chan/ui/animation/AnimationUtils.java index c516a675..1494f701 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/animation/AnimationUtils.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/animation/AnimationUtils.java @@ -17,180 +17,27 @@ */ package org.floens.chan.ui.animation; -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; import android.animation.ArgbEvaluator; import android.animation.ValueAnimator; import android.graphics.drawable.ColorDrawable; import android.view.View; -import android.view.ViewGroup; -import android.view.animation.DecelerateInterpolator; import android.widget.TextView; -import java.util.HashMap; -import java.util.Map; - public class AnimationUtils { public static int interpolate(int a, int b, float x) { return (int) (a + (b - a) * x); } - // a lot of these are deprecated, they animate the height with the layout params themselves, - // causing a measure loop for each frame. android just isn't designed for this, and it always - // lags. there are better ways to easily animate layouts, such as enabling the layoutAnimations - // flag. - - @Deprecated - public static void setHeight(View view, boolean expand, boolean animated) { - setHeight(view, expand, animated, -1); - } - - @Deprecated - public static void setHeight(View view, boolean expand, boolean animated, int knownWidth) { - if (animated) { - animateHeight(view, expand, knownWidth); - } else { - view.setVisibility(expand ? View.VISIBLE : View.GONE); - } - } - - private static Map layoutAnimations = new HashMap<>(); - - @Deprecated - public static int animateHeight(final View view, boolean expand) { - return animateHeight(view, expand, -1); - } - - @Deprecated - public static int animateHeight(final View view, final boolean expand, int knownWidth) { - return animateHeight(view, expand, knownWidth, 300); - } - - @Deprecated - public static int animateHeight(final View view, final boolean expand, int knownWidth, int duration) { - return animateHeight(view, expand, knownWidth, duration, null); - } - - /** - * Animate the height of a view by changing the layoutParams.height value.
- * view.measure is used to figure out the height. - * Use knownWidth when the width of the view has not been measured yet.
- * You can call this even when a height animation is currently running, it will resolve any issues.
- * This does cause some lag on complex views because requestLayout is called on each frame. - */ - @Deprecated - public static int animateHeight(final View view, final boolean expand, int knownWidth, int duration, final LayoutAnimationProgress progressCallback) { - final int fromHeight; - int toHeight; - if (expand) { - int width = knownWidth < 0 ? view.getWidth() : knownWidth; - - view.measure( - View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY), - View.MeasureSpec.UNSPECIFIED); - fromHeight = view.getHeight(); - toHeight = view.getMeasuredHeight(); - } else { - fromHeight = view.getHeight(); - toHeight = 0; - } - - animateLayout(true, view, fromHeight, toHeight, duration, true, progressCallback); - - return toHeight; - } - - @Deprecated - public static void animateLayout(final boolean vertical, final View view, final int from, final int to, int duration, final boolean wrapAfterwards, final LayoutAnimationProgress callback) { - ValueAnimator running = layoutAnimations.remove(view); - if (running != null) { - running.cancel(); - } - - ValueAnimator valueAnimator = ValueAnimator.ofInt(from, to); - valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - int value = (int) animation.getAnimatedValue(); - // Looks better - if (value == 1) { - value = 0; - } - if (vertical) { - view.getLayoutParams().height = value; - } else { - view.getLayoutParams().width = value; - } - view.requestLayout(); - - if (callback != null) { - callback.onLayoutAnimationProgress(view, vertical, from, to, value, animation.getAnimatedFraction()); - } - } - }); - - valueAnimator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationStart(Animator animation) { - view.setVisibility(View.VISIBLE); - } - - @Override - public void onAnimationEnd(Animator animation) { - if (to > 0) { - if (wrapAfterwards) { - if (vertical) { - view.getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT; - } else { - view.getLayoutParams().width = ViewGroup.LayoutParams.WRAP_CONTENT; - } - } - } else { - if (vertical) { - view.getLayoutParams().height = 0; - } else { - view.getLayoutParams().width = 0; - } - view.setVisibility(View.GONE); - } - view.requestLayout(); - - layoutAnimations.remove(view); - } - }); - valueAnimator.setInterpolator(new DecelerateInterpolator(2f)); - valueAnimator.setDuration(duration); - valueAnimator.start(); - - layoutAnimations.put(view, valueAnimator); - } - - @Deprecated - public interface LayoutAnimationProgress { - @Deprecated - void onLayoutAnimationProgress(View view, boolean vertical, int from, int to, int value, float progress); - } - public static void animateTextColor(final TextView text, int to) { ValueAnimator animation = ValueAnimator.ofObject(new ArgbEvaluator(), text.getCurrentTextColor(), to); - animation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - text.setTextColor((int) animation.getAnimatedValue()); - } - }); + animation.addUpdateListener(a -> text.setTextColor((int) a.getAnimatedValue())); animation.start(); } public static void animateBackgroundColorDrawable(final View view, int newColor) { int currentBackground = ((ColorDrawable) view.getBackground()).getColor(); ValueAnimator animation = ValueAnimator.ofObject(new ArgbEvaluator(), currentBackground, newColor); - animation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - view.setBackgroundColor((int) animation.getAnimatedValue()); - } - }); + animation.addUpdateListener(a -> view.setBackgroundColor((int) a.getAnimatedValue())); animation.start(); } } diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/MainSettingsController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/MainSettingsController.java index 1efcabb3..1588ac91 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/controller/MainSettingsController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/MainSettingsController.java @@ -34,7 +34,6 @@ import org.floens.chan.core.manager.BoardManager; import org.floens.chan.core.settings.ChanSettings; import org.floens.chan.core.site.Sites; import org.floens.chan.ui.activity.StartActivity; -import org.floens.chan.ui.animation.AnimationUtils; import org.floens.chan.ui.helper.HintPopup; import org.floens.chan.ui.helper.RefreshUIMessage; import org.floens.chan.ui.settings.BooleanSettingView; @@ -121,7 +120,7 @@ public class MainSettingsController extends SettingsController implements Toolba onPreferenceChange(imageAutoLoadView); if (!ChanSettings.developer.get()) { - developerView.view.getLayoutParams().height = 0; + developerView.view.setVisibility(View.GONE); } if (ChanSettings.settingsOpenCounter.increase() == 3) { @@ -353,7 +352,7 @@ public class MainSettingsController extends SettingsController implements Toolba Toast.makeText(context, (developer ? "Enabled" : "Disabled") + " developer options", Toast.LENGTH_LONG).show(); - AnimationUtils.animateHeight(developerView.view, developer); + developerView.view.setVisibility(developer ? View.VISIBLE : View.GONE); } } })); diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/PassSettingsController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/PassSettingsController.java index 87ca474a..b2644337 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/controller/PassSettingsController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/PassSettingsController.java @@ -37,7 +37,6 @@ import org.floens.chan.core.site.http.LoginRequest; import org.floens.chan.core.site.http.LoginResponse; import org.floens.chan.ui.view.CrossfadeView; import org.floens.chan.utils.AndroidUtils; -import org.floens.chan.ui.animation.AnimationUtils; import javax.inject.Inject; @@ -77,16 +76,16 @@ public class PassSettingsController extends Controller implements View.OnClickLi navigationItem.setTitle(R.string.settings_screen_pass); view = inflateRes(R.layout.controller_pass); - container = (LinearLayout) view.findViewById(R.id.container); - crossfadeView = (CrossfadeView) view.findViewById(R.id.crossfade); - errors = (TextView) view.findViewById(R.id.errors); - button = (Button) view.findViewById(R.id.button); - bottomDescription = (TextView) view.findViewById(R.id.bottom_description); - inputToken = (EditText) view.findViewById(R.id.input_token); - inputPin = (EditText) view.findViewById(R.id.input_pin); - authenticated = (TextView) view.findViewById(R.id.authenticated); + container = view.findViewById(R.id.container); + crossfadeView = view.findViewById(R.id.crossfade); + errors = view.findViewById(R.id.errors); + button = view.findViewById(R.id.button); + bottomDescription = view.findViewById(R.id.bottom_description); + inputToken = view.findViewById(R.id.input_token); + inputPin = view.findViewById(R.id.input_pin); + authenticated = view.findViewById(R.id.authenticated); - AnimationUtils.setHeight(errors, false, false); + errors.setVisibility(View.GONE); final boolean loggedIn = loggedIn(); button.setText(loggedIn ? R.string.setting_pass_logout : R.string.setting_pass_login); @@ -191,12 +190,12 @@ public class PassSettingsController extends Controller implements View.OnClickLi private void showError(String error) { errors.setText(error); - AnimationUtils.setHeight(errors, true, true, container.getWidth()); + errors.setVisibility(View.VISIBLE); } private void hideError() { errors.setText(null); - AnimationUtils.setHeight(errors, false, true, container.getHeight()); + errors.setVisibility(View.GONE); } private boolean loggedIn() { diff --git a/Clover/app/src/main/java/org/floens/chan/ui/layout/ReplyLayout.java b/Clover/app/src/main/java/org/floens/chan/ui/layout/ReplyLayout.java index 77e1e8af..4f7a1716 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/layout/ReplyLayout.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/layout/ReplyLayout.java @@ -345,11 +345,16 @@ public class ReplyLayout extends LoadView implements View.OnClickListener, Reply comment.setHint(hint); } + @Override + public void showCommentCounter(boolean show) { + commentCounter.setVisibility(show ? View.VISIBLE : View.GONE); + } + @Override public void setExpanded(boolean expanded) { setWrap(!expanded); - comment.setMaxLines(expanded ? 15 : 6); + comment.setMaxLines(expanded ? 500 : 6); preview.setLayoutParams(new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, diff --git a/Clover/app/src/main/java/org/floens/chan/ui/layout/ThreadListLayout.java b/Clover/app/src/main/java/org/floens/chan/ui/layout/ThreadListLayout.java index 32a524e5..0de92a64 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/layout/ThreadListLayout.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/layout/ThreadListLayout.java @@ -45,7 +45,6 @@ import org.floens.chan.core.presenter.ReplyPresenter; import org.floens.chan.core.settings.ChanSettings; import org.floens.chan.ui.adapter.PostAdapter; import org.floens.chan.ui.adapter.PostsFilter; -import org.floens.chan.ui.animation.AnimationUtils; import org.floens.chan.ui.cell.PostCell; import org.floens.chan.ui.cell.PostCellInterface; import org.floens.chan.ui.cell.ThreadStatusCell; @@ -81,7 +80,6 @@ public class ThreadListLayout extends FrameLayout implements ReplyLayout.ReplyLa private int background; private boolean searchOpen; private int lastPostCount; - private int recyclerViewTopPadding; private Handler mainHandler = new Handler(Looper.getMainLooper()); @@ -174,8 +172,7 @@ public class ThreadListLayout extends FrameLayout implements ReplyLayout.ReplyLa switch (postViewMode) { case LIST: LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext()); - recyclerViewTopPadding = 0; - recyclerView.setPadding(0, recyclerViewTopPadding + toolbarHeight(), 0, 0); + setRecyclerViewPadding(); recyclerView.setLayoutManager(linearLayoutManager); layoutManager = linearLayoutManager; @@ -187,8 +184,7 @@ public class ThreadListLayout extends FrameLayout implements ReplyLayout.ReplyLa break; case CARD: GridLayoutManager gridLayoutManager = new GridLayoutManager(null, spanCount, GridLayoutManager.VERTICAL, false); - recyclerViewTopPadding = dp(1); - recyclerView.setPadding(dp(1), recyclerViewTopPadding + toolbarHeight(), dp(1), dp(1)); + setRecyclerViewPadding(); recyclerView.setLayoutManager(gridLayoutManager); layoutManager = gridLayoutManager; @@ -292,11 +288,9 @@ public class ThreadListLayout extends FrameLayout implements ReplyLayout.ReplyLa } reply.onOpen(open); - if (open) { - recyclerView.setPadding(recyclerView.getPaddingLeft(), recyclerViewTopPadding + height, recyclerView.getPaddingRight(), recyclerView.getPaddingBottom()); - } else { + setRecyclerViewPadding(); + if (!open) { AndroidUtils.hideKeyboard(reply); - recyclerView.setPadding(recyclerView.getPaddingLeft(), recyclerViewTopPadding + toolbarHeight(), recyclerView.getPaddingRight(), recyclerView.getPaddingBottom()); } threadListLayoutCallback.replyLayoutOpen(open); @@ -312,19 +306,43 @@ public class ThreadListLayout extends FrameLayout implements ReplyLayout.ReplyLa postAdapter.showError(error); } - public void openSearch(boolean show) { - if (showingThread != null && searchOpen != show) { - searchOpen = show; - int height = AnimationUtils.animateHeight(searchStatus, show); + public void openSearch(boolean open) { + if (showingThread != null && searchOpen != open) { + searchOpen = open; - if (show) { - searchStatus.setText(R.string.search_empty); - recyclerView.setPadding(recyclerView.getPaddingLeft(), recyclerViewTopPadding + height, recyclerView.getPaddingRight(), recyclerView.getPaddingBottom()); + searchStatus.measure( + MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED) + ); + int height = searchStatus.getMeasuredHeight(); + + final ViewPropertyAnimator viewPropertyAnimator = searchStatus.animate(); + viewPropertyAnimator.setListener(null); + viewPropertyAnimator.setInterpolator(new DecelerateInterpolator(2f)); + viewPropertyAnimator.setDuration(600); + + if (open) { + searchStatus.setVisibility(View.VISIBLE); + searchStatus.setTranslationY(-height); + viewPropertyAnimator.translationY(0f); } else { - recyclerView.setPadding(recyclerView.getPaddingLeft(), recyclerViewTopPadding + toolbarHeight(), recyclerView.getPaddingRight(), recyclerView.getPaddingBottom()); + searchStatus.setTranslationY(0f); + viewPropertyAnimator.translationY(-height); + viewPropertyAnimator.setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + viewPropertyAnimator.setListener(null); + searchStatus.setVisibility(View.GONE); + } + }); } - attachToolbarScroll(!(show || replyOpen)); + setRecyclerViewPadding(); + if (open) { + searchStatus.setText(R.string.search_empty); + } + + attachToolbarScroll(!(open || replyOpen)); } } @@ -512,6 +530,36 @@ public class ThreadListLayout extends FrameLayout implements ReplyLayout.ReplyLa } } + private void setRecyclerViewPadding() { + int defaultPadding = 0; + if (postViewMode == ChanSettings.PostViewMode.CARD) { + defaultPadding = dp(1); + } + + int left = defaultPadding; + int top = defaultPadding; + int right = defaultPadding; + int bottom = defaultPadding; + + if (replyOpen) { + reply.measure( + MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED) + ); + top += reply.getMeasuredHeight(); + } else if (searchOpen) { + searchStatus.measure( + MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED) + ); + top += searchStatus.getMeasuredHeight(); + } else { + top += toolbarHeight(); + } + + recyclerView.setPadding(left, top, right, bottom); + } + private int toolbarHeight() { Toolbar toolbar = threadListLayoutCallback.getToolbar(); return toolbar.getToolbarHeight(); diff --git a/Clover/app/src/main/java/org/floens/chan/ui/settings/SettingsController.java b/Clover/app/src/main/java/org/floens/chan/ui/settings/SettingsController.java index c2aa91b6..012c5564 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/settings/SettingsController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/settings/SettingsController.java @@ -28,7 +28,6 @@ import android.widget.TextView; import org.floens.chan.R; import org.floens.chan.controller.Controller; import org.floens.chan.utils.AndroidUtils; -import org.floens.chan.ui.animation.AnimationUtils; import java.util.ArrayList; import java.util.List; @@ -101,11 +100,7 @@ public class SettingsController extends Controller implements AndroidUtils.OnMea } protected void setSettingViewVisibility(SettingView settingView, boolean visible, boolean animated) { - if (animated) { - AnimationUtils.animateHeight(settingView.view, visible, ((View) settingView.view.getParent()).getWidth()); - } else { - settingView.view.setVisibility(visible ? View.VISIBLE : View.GONE); - } + settingView.view.setVisibility(visible ? View.VISIBLE : View.GONE); if (settingView.divider != null) { settingView.divider.setVisibility(visible ? View.VISIBLE : View.GONE); @@ -163,19 +158,8 @@ public class SettingsController extends Controller implements AndroidUtils.OnMea final TextView bottom = ((TextView) view.findViewById(R.id.bottom)); if (bottom != null) { - if (built) { - if (bottomText != null) { - bottom.setText(bottomText); - } - - // This way of animating never works on textviews if they're just added. - if (bottom.getHeight() != 0) { - AnimationUtils.animateHeight(bottom, bottomText != null, ((View) view.getParent()).getWidth()); - } - } else { - bottom.setText(bottomText); - bottom.setVisibility(bottomText == null ? View.GONE : View.VISIBLE); - } + bottom.setText(bottomText); + bottom.setVisibility(bottomText == null ? View.GONE : View.VISIBLE); } } } diff --git a/Clover/app/src/main/res/layout/controller_pass.xml b/Clover/app/src/main/res/layout/controller_pass.xml index de5e56ca..7833451e 100644 --- a/Clover/app/src/main/res/layout/controller_pass.xml +++ b/Clover/app/src/main/res/layout/controller_pass.xml @@ -24,6 +24,7 @@ along with this program. If not, see . android:id="@+id/container" android:layout_width="match_parent" android:layout_height="wrap_content" + android:animateLayoutChanges="true" android:orientation="vertical" android:paddingBottom="32dp" android:paddingLeft="32dp" @@ -39,7 +40,8 @@ along with this program. If not, see . android:paddingRight="4dp" android:paddingTop="8dp" android:textColor="#fff44336" - android:textSize="16sp" /> + android:textSize="16sp" + android:visibility="gone" /> .