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
multisite
Floens 8 years ago
parent 077d3167a0
commit 759744984e
  1. 1
      Clover/app/src/main/java/org/floens/chan/core/model/orm/Filter.java
  2. 3
      Clover/app/src/main/java/org/floens/chan/core/presenter/ReplyPresenter.java
  3. 157
      Clover/app/src/main/java/org/floens/chan/ui/animation/AnimationUtils.java
  4. 5
      Clover/app/src/main/java/org/floens/chan/ui/controller/MainSettingsController.java
  5. 23
      Clover/app/src/main/java/org/floens/chan/ui/controller/PassSettingsController.java
  6. 7
      Clover/app/src/main/java/org/floens/chan/ui/layout/ReplyLayout.java
  7. 86
      Clover/app/src/main/java/org/floens/chan/ui/layout/ThreadListLayout.java
  8. 22
      Clover/app/src/main/java/org/floens/chan/ui/settings/SettingsController.java
  9. 4
      Clover/app/src/main/res/layout/controller_pass.xml
  10. 2
      Clover/app/src/main/res/layout/layout_thread_list.xml

@ -40,7 +40,6 @@ public class Filter {
@DatabaseField(canBeNull = false)
public boolean allBoards = true;
// TODO(multi-site)
@DatabaseField(canBeNull = false)
public String boards;

@ -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);

@ -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<View, ValueAnimator> 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.<br>
* view.measure is used to figure out the height.
* Use knownWidth when the width of the view has not been measured yet.<br>
* You can call this even when a height animation is currently running, it will resolve any issues.<br>
* <b>This does cause some lag on complex views because requestLayout is called on each frame.</b>
*/
@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();
}
}

@ -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);
}
}
}));

@ -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() {

@ -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,

@ -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();

@ -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);
}
}
}

@ -24,6 +24,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
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 <http://www.gnu.org/licenses/>.
android:paddingRight="4dp"
android:paddingTop="8dp"
android:textColor="#fff44336"
android:textSize="16sp" />
android:textSize="16sp"
android:visibility="gone" />
<org.floens.chan.ui.view.CrossfadeView
android:id="@+id/crossfade"

@ -40,7 +40,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<TextView
android:id="@+id/search_status"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_height="wrap_content"
android:background="?backcolor"
android:elevation="4dp"
android:gravity="center"

Loading…
Cancel
Save