mirror of
https://github.com/Dark98/SliceBeam.git
synced 2026-07-06 16:49:05 +00:00
Public source code release
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
package ru.ytkab0bp.slicebeam.recycler;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Gravity;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
|
||||
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
|
||||
|
||||
public class BigHeaderItem extends SimpleRecyclerItem<TextView> {
|
||||
public String title;
|
||||
|
||||
public BigHeaderItem() {}
|
||||
|
||||
public BigHeaderItem(String t) {
|
||||
title = t;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextView onCreateView(Context ctx) {
|
||||
TextView tv = new TextView(ctx);
|
||||
tv.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20);
|
||||
tv.setTypeface(ViewUtils.getTypeface(ViewUtils.ROBOTO_MEDIUM));
|
||||
tv.setTextColor(ThemesRepo.getColor(android.R.attr.textColorPrimary));
|
||||
tv.setPadding(ViewUtils.dp(21), ViewUtils.dp(12), ViewUtils.dp(21), ViewUtils.dp(12));
|
||||
tv.setGravity(Gravity.START);
|
||||
tv.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||
return tv;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindView(TextView view) {
|
||||
view.setText(title);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,673 @@
|
||||
/*
|
||||
* Copyright 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ru.ytkab0bp.slicebeam.recycler;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.TimeInterpolator;
|
||||
import android.view.View;
|
||||
import android.view.ViewPropertyAnimator;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.recyclerview.widget.SimpleItemAnimator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
|
||||
|
||||
/**
|
||||
* This implementation of {@link RecyclerView.ItemAnimator} provides basic
|
||||
* animations on remove, add, and move events that happen to the items in
|
||||
* a RecyclerView. RecyclerView uses a DefaultItemAnimator by default.
|
||||
*
|
||||
* @see RecyclerView#setItemAnimator(RecyclerView.ItemAnimator)
|
||||
*/
|
||||
public class CubicBezierItemAnimator extends SimpleItemAnimator {
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
private static TimeInterpolator sDefaultInterpolator = ViewUtils.CUBIC_INTERPOLATOR;
|
||||
|
||||
private ArrayList<RecyclerView.ViewHolder> mPendingRemovals = new ArrayList<>();
|
||||
private ArrayList<RecyclerView.ViewHolder> mPendingAdditions = new ArrayList<>();
|
||||
private ArrayList<MoveInfo> mPendingMoves = new ArrayList<>();
|
||||
private ArrayList<ChangeInfo> mPendingChanges = new ArrayList<>();
|
||||
|
||||
ArrayList<ArrayList<RecyclerView.ViewHolder>> mAdditionsList = new ArrayList<>();
|
||||
ArrayList<ArrayList<MoveInfo>> mMovesList = new ArrayList<>();
|
||||
ArrayList<ArrayList<ChangeInfo>> mChangesList = new ArrayList<>();
|
||||
|
||||
ArrayList<RecyclerView.ViewHolder> mAddAnimations = new ArrayList<>();
|
||||
ArrayList<RecyclerView.ViewHolder> mMoveAnimations = new ArrayList<>();
|
||||
ArrayList<RecyclerView.ViewHolder> mRemoveAnimations = new ArrayList<>();
|
||||
ArrayList<RecyclerView.ViewHolder> mChangeAnimations = new ArrayList<>();
|
||||
|
||||
private static class MoveInfo {
|
||||
public RecyclerView.ViewHolder holder;
|
||||
public int fromX, fromY, toX, toY;
|
||||
|
||||
MoveInfo(RecyclerView.ViewHolder holder, int fromX, int fromY, int toX, int toY) {
|
||||
this.holder = holder;
|
||||
this.fromX = fromX;
|
||||
this.fromY = fromY;
|
||||
this.toX = toX;
|
||||
this.toY = toY;
|
||||
}
|
||||
}
|
||||
|
||||
private static class ChangeInfo {
|
||||
public RecyclerView.ViewHolder oldHolder, newHolder;
|
||||
public int fromX, fromY, toX, toY;
|
||||
private ChangeInfo(RecyclerView.ViewHolder oldHolder, RecyclerView.ViewHolder newHolder) {
|
||||
this.oldHolder = oldHolder;
|
||||
this.newHolder = newHolder;
|
||||
}
|
||||
|
||||
ChangeInfo(RecyclerView.ViewHolder oldHolder, RecyclerView.ViewHolder newHolder,
|
||||
int fromX, int fromY, int toX, int toY) {
|
||||
this(oldHolder, newHolder);
|
||||
this.fromX = fromX;
|
||||
this.fromY = fromY;
|
||||
this.toX = toX;
|
||||
this.toY = toY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ChangeInfo{"
|
||||
+ "oldHolder=" + oldHolder
|
||||
+ ", newHolder=" + newHolder
|
||||
+ ", fromX=" + fromX
|
||||
+ ", fromY=" + fromY
|
||||
+ ", toX=" + toX
|
||||
+ ", toY=" + toY
|
||||
+ '}';
|
||||
}
|
||||
}
|
||||
|
||||
public CubicBezierItemAnimator() {
|
||||
int duration = 180;
|
||||
setAddDuration(duration);
|
||||
setRemoveDuration(duration);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runPendingAnimations() {
|
||||
boolean removalsPending = !mPendingRemovals.isEmpty();
|
||||
boolean movesPending = !mPendingMoves.isEmpty();
|
||||
boolean changesPending = !mPendingChanges.isEmpty();
|
||||
boolean additionsPending = !mPendingAdditions.isEmpty();
|
||||
if (!removalsPending && !movesPending && !additionsPending && !changesPending) {
|
||||
// nothing to animate
|
||||
return;
|
||||
}
|
||||
// First, remove stuff
|
||||
for (RecyclerView.ViewHolder holder : mPendingRemovals) {
|
||||
animateRemoveImpl(holder);
|
||||
}
|
||||
mPendingRemovals.clear();
|
||||
// Next, move stuff
|
||||
if (movesPending) {
|
||||
final ArrayList<MoveInfo> moves = new ArrayList<>();
|
||||
moves.addAll(mPendingMoves);
|
||||
mMovesList.add(moves);
|
||||
mPendingMoves.clear();
|
||||
Runnable mover = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
for (MoveInfo moveInfo : moves) {
|
||||
animateMoveImpl(moveInfo.holder, moveInfo.fromX, moveInfo.fromY,
|
||||
moveInfo.toX, moveInfo.toY);
|
||||
}
|
||||
moves.clear();
|
||||
mMovesList.remove(moves);
|
||||
}
|
||||
};
|
||||
if (removalsPending) {
|
||||
View view = moves.get(0).holder.itemView;
|
||||
ViewCompat.postOnAnimationDelayed(view, mover, getRemoveDuration());
|
||||
} else {
|
||||
mover.run();
|
||||
}
|
||||
}
|
||||
// Next, change stuff, to run in parallel with move animations
|
||||
if (changesPending) {
|
||||
final ArrayList<ChangeInfo> changes = new ArrayList<>();
|
||||
changes.addAll(mPendingChanges);
|
||||
mChangesList.add(changes);
|
||||
mPendingChanges.clear();
|
||||
Runnable changer = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
for (ChangeInfo change : changes) {
|
||||
animateChangeImpl(change);
|
||||
}
|
||||
changes.clear();
|
||||
mChangesList.remove(changes);
|
||||
}
|
||||
};
|
||||
if (removalsPending) {
|
||||
RecyclerView.ViewHolder holder = changes.get(0).oldHolder;
|
||||
ViewCompat.postOnAnimationDelayed(holder.itemView, changer, getRemoveDuration());
|
||||
} else {
|
||||
changer.run();
|
||||
}
|
||||
}
|
||||
// Next, add stuff
|
||||
if (additionsPending) {
|
||||
final ArrayList<RecyclerView.ViewHolder> additions = new ArrayList<>();
|
||||
additions.addAll(mPendingAdditions);
|
||||
mAdditionsList.add(additions);
|
||||
mPendingAdditions.clear();
|
||||
Runnable adder = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
for (RecyclerView.ViewHolder holder : additions) {
|
||||
animateAddImpl(holder);
|
||||
}
|
||||
additions.clear();
|
||||
mAdditionsList.remove(additions);
|
||||
}
|
||||
};
|
||||
if (removalsPending || movesPending || changesPending) {
|
||||
long removeDuration = removalsPending ? getRemoveDuration() : 0;
|
||||
long moveDuration = movesPending ? getMoveDuration() : 0;
|
||||
long changeDuration = changesPending ? getChangeDuration() : 0;
|
||||
long totalDelay = removeDuration + Math.max(moveDuration, changeDuration);
|
||||
View view = additions.get(0).itemView;
|
||||
ViewCompat.postOnAnimationDelayed(view, adder, totalDelay);
|
||||
} else {
|
||||
adder.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean animateRemove(final RecyclerView.ViewHolder holder) {
|
||||
resetAnimation(holder);
|
||||
mPendingRemovals.add(holder);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void animateRemoveImpl(final RecyclerView.ViewHolder holder) {
|
||||
final View view = holder.itemView;
|
||||
final ViewPropertyAnimator animation = view.animate();
|
||||
mRemoveAnimations.add(holder);
|
||||
animation.setDuration(getRemoveDuration()).alpha(0).setListener(
|
||||
new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationStart(Animator animator) {
|
||||
dispatchRemoveStarting(holder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animator) {
|
||||
animation.setListener(null);
|
||||
view.setAlpha(1);
|
||||
dispatchRemoveFinished(holder);
|
||||
mRemoveAnimations.remove(holder);
|
||||
dispatchFinishedWhenDone();
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean animateAdd(final RecyclerView.ViewHolder holder) {
|
||||
resetAnimation(holder);
|
||||
holder.itemView.setAlpha(0);
|
||||
mPendingAdditions.add(holder);
|
||||
return true;
|
||||
}
|
||||
|
||||
void animateAddImpl(final RecyclerView.ViewHolder holder) {
|
||||
final View view = holder.itemView;
|
||||
final ViewPropertyAnimator animation = view.animate();
|
||||
mAddAnimations.add(holder);
|
||||
animation.alpha(1).setDuration(getAddDuration())
|
||||
.setListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationStart(Animator animator) {
|
||||
dispatchAddStarting(holder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animator) {
|
||||
view.setAlpha(1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animator) {
|
||||
animation.setListener(null);
|
||||
dispatchAddFinished(holder);
|
||||
mAddAnimations.remove(holder);
|
||||
dispatchFinishedWhenDone();
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean animateMove(final RecyclerView.ViewHolder holder, int fromX, int fromY,
|
||||
int toX, int toY) {
|
||||
final View view = holder.itemView;
|
||||
fromX += (int) holder.itemView.getTranslationX();
|
||||
fromY += (int) holder.itemView.getTranslationY();
|
||||
resetAnimation(holder);
|
||||
int deltaX = toX - fromX;
|
||||
int deltaY = toY - fromY;
|
||||
if (deltaX == 0 && deltaY == 0) {
|
||||
dispatchMoveFinished(holder);
|
||||
return false;
|
||||
}
|
||||
if (deltaX != 0) {
|
||||
view.setTranslationX(-deltaX);
|
||||
}
|
||||
if (deltaY != 0) {
|
||||
view.setTranslationY(-deltaY);
|
||||
}
|
||||
mPendingMoves.add(new MoveInfo(holder, fromX, fromY, toX, toY));
|
||||
return true;
|
||||
}
|
||||
|
||||
void animateMoveImpl(final RecyclerView.ViewHolder holder, int fromX, int fromY, int toX, int toY) {
|
||||
final View view = holder.itemView;
|
||||
final int deltaX = toX - fromX;
|
||||
final int deltaY = toY - fromY;
|
||||
if (deltaX != 0) {
|
||||
view.animate().translationX(0);
|
||||
}
|
||||
if (deltaY != 0) {
|
||||
view.animate().translationY(0);
|
||||
}
|
||||
// TODO: make EndActions end listeners instead, since end actions aren't called when
|
||||
// vpas are canceled (and can't end them. why?)
|
||||
// need listener functionality in VPACompat for this. Ick.
|
||||
final ViewPropertyAnimator animation = view.animate();
|
||||
mMoveAnimations.add(holder);
|
||||
animation.setDuration(getMoveDuration()).setListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationStart(Animator animator) {
|
||||
dispatchMoveStarting(holder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animator) {
|
||||
if (deltaX != 0) {
|
||||
view.setTranslationX(0);
|
||||
}
|
||||
if (deltaY != 0) {
|
||||
view.setTranslationY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animator) {
|
||||
animation.setListener(null);
|
||||
dispatchMoveFinished(holder);
|
||||
mMoveAnimations.remove(holder);
|
||||
dispatchFinishedWhenDone();
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean animateChange(RecyclerView.ViewHolder oldHolder, RecyclerView.ViewHolder newHolder,
|
||||
int fromX, int fromY, int toX, int toY) {
|
||||
if (oldHolder == newHolder) {
|
||||
// Don't know how to run change animations when the same view holder is re-used.
|
||||
// run a move animation to handle position changes.
|
||||
return animateMove(oldHolder, fromX, fromY, toX, toY);
|
||||
}
|
||||
final float prevTranslationX = oldHolder.itemView.getTranslationX();
|
||||
final float prevTranslationY = oldHolder.itemView.getTranslationY();
|
||||
final float prevAlpha = oldHolder.itemView.getAlpha();
|
||||
resetAnimation(oldHolder);
|
||||
int deltaX = (int) (toX - fromX - prevTranslationX);
|
||||
int deltaY = (int) (toY - fromY - prevTranslationY);
|
||||
// recover prev translation state after ending animation
|
||||
oldHolder.itemView.setTranslationX(prevTranslationX);
|
||||
oldHolder.itemView.setTranslationY(prevTranslationY);
|
||||
oldHolder.itemView.setAlpha(prevAlpha);
|
||||
if (newHolder != null) {
|
||||
// carry over translation values
|
||||
resetAnimation(newHolder);
|
||||
newHolder.itemView.setTranslationX(-deltaX);
|
||||
newHolder.itemView.setTranslationY(-deltaY);
|
||||
newHolder.itemView.setAlpha(0);
|
||||
}
|
||||
mPendingChanges.add(new ChangeInfo(oldHolder, newHolder, fromX, fromY, toX, toY));
|
||||
return true;
|
||||
}
|
||||
|
||||
void animateChangeImpl(final ChangeInfo changeInfo) {
|
||||
final RecyclerView.ViewHolder holder = changeInfo.oldHolder;
|
||||
final View view = holder == null ? null : holder.itemView;
|
||||
final RecyclerView.ViewHolder newHolder = changeInfo.newHolder;
|
||||
final View newView = newHolder != null ? newHolder.itemView : null;
|
||||
if (view != null) {
|
||||
final ViewPropertyAnimator oldViewAnim = view.animate().setDuration(
|
||||
getChangeDuration());
|
||||
mChangeAnimations.add(changeInfo.oldHolder);
|
||||
oldViewAnim.translationX(changeInfo.toX - changeInfo.fromX);
|
||||
oldViewAnim.translationY(changeInfo.toY - changeInfo.fromY);
|
||||
oldViewAnim.alpha(0).setListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationStart(Animator animator) {
|
||||
dispatchChangeStarting(changeInfo.oldHolder, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animator) {
|
||||
oldViewAnim.setListener(null);
|
||||
view.setAlpha(1);
|
||||
view.setTranslationX(0);
|
||||
view.setTranslationY(0);
|
||||
dispatchChangeFinished(changeInfo.oldHolder, true);
|
||||
mChangeAnimations.remove(changeInfo.oldHolder);
|
||||
dispatchFinishedWhenDone();
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
if (newView != null) {
|
||||
final ViewPropertyAnimator newViewAnimation = newView.animate();
|
||||
mChangeAnimations.add(changeInfo.newHolder);
|
||||
newViewAnimation.translationX(0).translationY(0).setDuration(getChangeDuration())
|
||||
.alpha(1).setListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationStart(Animator animator) {
|
||||
dispatchChangeStarting(changeInfo.newHolder, false);
|
||||
}
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animator) {
|
||||
newViewAnimation.setListener(null);
|
||||
newView.setAlpha(1);
|
||||
newView.setTranslationX(0);
|
||||
newView.setTranslationY(0);
|
||||
dispatchChangeFinished(changeInfo.newHolder, false);
|
||||
mChangeAnimations.remove(changeInfo.newHolder);
|
||||
dispatchFinishedWhenDone();
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
}
|
||||
|
||||
private void endChangeAnimation(List<ChangeInfo> infoList, RecyclerView.ViewHolder item) {
|
||||
for (int i = infoList.size() - 1; i >= 0; i--) {
|
||||
ChangeInfo changeInfo = infoList.get(i);
|
||||
if (endChangeAnimationIfNecessary(changeInfo, item)) {
|
||||
if (changeInfo.oldHolder == null && changeInfo.newHolder == null) {
|
||||
infoList.remove(changeInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void endChangeAnimationIfNecessary(ChangeInfo changeInfo) {
|
||||
if (changeInfo.oldHolder != null) {
|
||||
endChangeAnimationIfNecessary(changeInfo, changeInfo.oldHolder);
|
||||
}
|
||||
if (changeInfo.newHolder != null) {
|
||||
endChangeAnimationIfNecessary(changeInfo, changeInfo.newHolder);
|
||||
}
|
||||
}
|
||||
private boolean endChangeAnimationIfNecessary(ChangeInfo changeInfo, RecyclerView.ViewHolder item) {
|
||||
boolean oldItem = false;
|
||||
if (changeInfo.newHolder == item) {
|
||||
changeInfo.newHolder = null;
|
||||
} else if (changeInfo.oldHolder == item) {
|
||||
changeInfo.oldHolder = null;
|
||||
oldItem = true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
item.itemView.setAlpha(1);
|
||||
item.itemView.setTranslationX(0);
|
||||
item.itemView.setTranslationY(0);
|
||||
dispatchChangeFinished(item, oldItem);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endAnimation(RecyclerView.ViewHolder item) {
|
||||
final View view = item.itemView;
|
||||
// this will trigger end callback which should set properties to their target values.
|
||||
view.animate().cancel();
|
||||
// TODO if some other animations are chained to end, how do we cancel them as well?
|
||||
for (int i = mPendingMoves.size() - 1; i >= 0; i--) {
|
||||
MoveInfo moveInfo = mPendingMoves.get(i);
|
||||
if (moveInfo.holder == item) {
|
||||
view.setTranslationY(0);
|
||||
view.setTranslationX(0);
|
||||
dispatchMoveFinished(item);
|
||||
mPendingMoves.remove(i);
|
||||
}
|
||||
}
|
||||
endChangeAnimation(mPendingChanges, item);
|
||||
if (mPendingRemovals.remove(item)) {
|
||||
view.setAlpha(1);
|
||||
dispatchRemoveFinished(item);
|
||||
}
|
||||
if (mPendingAdditions.remove(item)) {
|
||||
view.setAlpha(1);
|
||||
dispatchAddFinished(item);
|
||||
}
|
||||
|
||||
for (int i = mChangesList.size() - 1; i >= 0; i--) {
|
||||
ArrayList<ChangeInfo> changes = mChangesList.get(i);
|
||||
endChangeAnimation(changes, item);
|
||||
if (changes.isEmpty()) {
|
||||
mChangesList.remove(i);
|
||||
}
|
||||
}
|
||||
for (int i = mMovesList.size() - 1; i >= 0; i--) {
|
||||
ArrayList<MoveInfo> moves = mMovesList.get(i);
|
||||
for (int j = moves.size() - 1; j >= 0; j--) {
|
||||
MoveInfo moveInfo = moves.get(j);
|
||||
if (moveInfo.holder == item) {
|
||||
view.setTranslationY(0);
|
||||
view.setTranslationX(0);
|
||||
dispatchMoveFinished(item);
|
||||
moves.remove(j);
|
||||
if (moves.isEmpty()) {
|
||||
mMovesList.remove(i);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int i = mAdditionsList.size() - 1; i >= 0; i--) {
|
||||
ArrayList<RecyclerView.ViewHolder> additions = mAdditionsList.get(i);
|
||||
if (additions.remove(item)) {
|
||||
view.setAlpha(1);
|
||||
dispatchAddFinished(item);
|
||||
if (additions.isEmpty()) {
|
||||
mAdditionsList.remove(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// animations should be ended by the cancel above.
|
||||
//noinspection PointlessBooleanExpression,ConstantConditions
|
||||
if (mRemoveAnimations.remove(item) && DEBUG) {
|
||||
throw new IllegalStateException("after animation is cancelled, item should not be in "
|
||||
+ "mRemoveAnimations list");
|
||||
}
|
||||
|
||||
//noinspection PointlessBooleanExpression,ConstantConditions
|
||||
if (mAddAnimations.remove(item) && DEBUG) {
|
||||
throw new IllegalStateException("after animation is cancelled, item should not be in "
|
||||
+ "mAddAnimations list");
|
||||
}
|
||||
|
||||
//noinspection PointlessBooleanExpression,ConstantConditions
|
||||
if (mChangeAnimations.remove(item) && DEBUG) {
|
||||
throw new IllegalStateException("after animation is cancelled, item should not be in "
|
||||
+ "mChangeAnimations list");
|
||||
}
|
||||
|
||||
//noinspection PointlessBooleanExpression,ConstantConditions
|
||||
if (mMoveAnimations.remove(item) && DEBUG) {
|
||||
throw new IllegalStateException("after animation is cancelled, item should not be in "
|
||||
+ "mMoveAnimations list");
|
||||
}
|
||||
dispatchFinishedWhenDone();
|
||||
}
|
||||
|
||||
private void resetAnimation(RecyclerView.ViewHolder holder) {
|
||||
holder.itemView.animate().setInterpolator(sDefaultInterpolator);
|
||||
endAnimation(holder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRunning() {
|
||||
return (!mPendingAdditions.isEmpty()
|
||||
|| !mPendingChanges.isEmpty()
|
||||
|| !mPendingMoves.isEmpty()
|
||||
|| !mPendingRemovals.isEmpty()
|
||||
|| !mMoveAnimations.isEmpty()
|
||||
|| !mRemoveAnimations.isEmpty()
|
||||
|| !mAddAnimations.isEmpty()
|
||||
|| !mChangeAnimations.isEmpty()
|
||||
|| !mMovesList.isEmpty()
|
||||
|| !mAdditionsList.isEmpty()
|
||||
|| !mChangesList.isEmpty());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the state of currently pending and running animations. If there are none
|
||||
* pending/running, call {@link #dispatchAnimationsFinished()} to notify any
|
||||
* listeners.
|
||||
*/
|
||||
void dispatchFinishedWhenDone() {
|
||||
if (!isRunning()) {
|
||||
dispatchAnimationsFinished();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endAnimations() {
|
||||
int count = mPendingMoves.size();
|
||||
for (int i = count - 1; i >= 0; i--) {
|
||||
MoveInfo item = mPendingMoves.get(i);
|
||||
View view = item.holder.itemView;
|
||||
view.setTranslationY(0);
|
||||
view.setTranslationX(0);
|
||||
dispatchMoveFinished(item.holder);
|
||||
mPendingMoves.remove(i);
|
||||
}
|
||||
count = mPendingRemovals.size();
|
||||
for (int i = count - 1; i >= 0; i--) {
|
||||
RecyclerView.ViewHolder item = mPendingRemovals.get(i);
|
||||
dispatchRemoveFinished(item);
|
||||
mPendingRemovals.remove(i);
|
||||
}
|
||||
count = mPendingAdditions.size();
|
||||
for (int i = count - 1; i >= 0; i--) {
|
||||
RecyclerView.ViewHolder item = mPendingAdditions.get(i);
|
||||
item.itemView.setAlpha(1);
|
||||
dispatchAddFinished(item);
|
||||
mPendingAdditions.remove(i);
|
||||
}
|
||||
count = mPendingChanges.size();
|
||||
for (int i = count - 1; i >= 0; i--) {
|
||||
endChangeAnimationIfNecessary(mPendingChanges.get(i));
|
||||
}
|
||||
mPendingChanges.clear();
|
||||
if (!isRunning()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int listCount = mMovesList.size();
|
||||
for (int i = listCount - 1; i >= 0; i--) {
|
||||
ArrayList<MoveInfo> moves = mMovesList.get(i);
|
||||
count = moves.size();
|
||||
for (int j = count - 1; j >= 0; j--) {
|
||||
MoveInfo moveInfo = moves.get(j);
|
||||
RecyclerView.ViewHolder item = moveInfo.holder;
|
||||
View view = item.itemView;
|
||||
view.setTranslationY(0);
|
||||
view.setTranslationX(0);
|
||||
dispatchMoveFinished(moveInfo.holder);
|
||||
moves.remove(j);
|
||||
if (moves.isEmpty()) {
|
||||
mMovesList.remove(moves);
|
||||
}
|
||||
}
|
||||
}
|
||||
listCount = mAdditionsList.size();
|
||||
for (int i = listCount - 1; i >= 0; i--) {
|
||||
ArrayList<RecyclerView.ViewHolder> additions = mAdditionsList.get(i);
|
||||
count = additions.size();
|
||||
for (int j = count - 1; j >= 0; j--) {
|
||||
RecyclerView.ViewHolder item = additions.get(j);
|
||||
View view = item.itemView;
|
||||
view.setAlpha(1);
|
||||
dispatchAddFinished(item);
|
||||
additions.remove(j);
|
||||
if (additions.isEmpty()) {
|
||||
mAdditionsList.remove(additions);
|
||||
}
|
||||
}
|
||||
}
|
||||
listCount = mChangesList.size();
|
||||
for (int i = listCount - 1; i >= 0; i--) {
|
||||
ArrayList<ChangeInfo> changes = mChangesList.get(i);
|
||||
count = changes.size();
|
||||
for (int j = count - 1; j >= 0; j--) {
|
||||
endChangeAnimationIfNecessary(changes.get(j));
|
||||
if (changes.isEmpty()) {
|
||||
mChangesList.remove(changes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cancelAll(mRemoveAnimations);
|
||||
cancelAll(mMoveAnimations);
|
||||
cancelAll(mAddAnimations);
|
||||
cancelAll(mChangeAnimations);
|
||||
|
||||
dispatchAnimationsFinished();
|
||||
}
|
||||
|
||||
void cancelAll(List<RecyclerView.ViewHolder> viewHolders) {
|
||||
for (int i = viewHolders.size() - 1; i >= 0; i--) {
|
||||
viewHolders.get(i).itemView.animate().cancel();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>
|
||||
* If the payload list is not empty, DefaultItemAnimator returns <code>true</code>.
|
||||
* When this is the case:
|
||||
* <ul>
|
||||
* <li>If you override {@link #animateChange(RecyclerView.ViewHolder, RecyclerView.ViewHolder, int, int, int, int)}, both
|
||||
* ViewHolder arguments will be the same instance.
|
||||
* </li>
|
||||
* <li>
|
||||
* If you are not overriding {@link #animateChange(RecyclerView.ViewHolder, RecyclerView.ViewHolder, int, int, int, int)},
|
||||
* then DefaultItemAnimator will call {@link #animateMove(RecyclerView.ViewHolder, int, int, int, int)} and
|
||||
* run a move animation instead.
|
||||
* </li>
|
||||
* </ul>
|
||||
*/
|
||||
@Override
|
||||
public boolean canReuseUpdatedViewHolder(@NonNull RecyclerView.ViewHolder viewHolder,
|
||||
@NonNull List<Object> payloads) {
|
||||
return !payloads.isEmpty() || super.canReuseUpdatedViewHolder(viewHolder, payloads);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package ru.ytkab0bp.slicebeam.recycler;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
|
||||
import ru.ytkab0bp.slicebeam.view.DividerView;
|
||||
|
||||
public class DividerItem extends SimpleRecyclerItem<DividerView> {
|
||||
@Override
|
||||
public DividerView onCreateView(Context ctx) {
|
||||
DividerView v = new DividerView(ctx);
|
||||
v.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewUtils.dp(1)) {{
|
||||
leftMargin = rightMargin = ViewUtils.dp(16);
|
||||
topMargin = bottomMargin = ViewUtils.dp(6);
|
||||
}});
|
||||
return v;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
package ru.ytkab0bp.slicebeam.recycler;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.graphics.Typeface;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.text.TextUtils;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import ru.ytkab0bp.slicebeam.SliceBeam;
|
||||
import ru.ytkab0bp.slicebeam.theme.IThemeView;
|
||||
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
|
||||
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
|
||||
|
||||
public class PreferenceItem extends SimpleRecyclerItem<PreferenceItem.PreferenceHolderView> {
|
||||
private Drawable mIcon;
|
||||
private CharSequence mTitle;
|
||||
private ValueProvider mSubtitle;
|
||||
private View.OnClickListener onClickListener;
|
||||
private int textColorRes;
|
||||
private boolean noTint;
|
||||
private ValueProvider valueProvider;
|
||||
|
||||
public PreferenceItem setTitle(CharSequence title) {
|
||||
mTitle = title;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PreferenceItem setSubtitle(CharSequence subtitle) {
|
||||
mSubtitle = ()->subtitle;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PreferenceItem setSubtitleProvider(ValueProvider mSubtitle) {
|
||||
this.mSubtitle = mSubtitle;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PreferenceItem setValueProvider(ValueProvider valueProvider) {
|
||||
this.valueProvider = valueProvider;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PreferenceItem setValue(String text) {
|
||||
this.valueProvider = () -> text;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PreferenceItem setIcon(int iconRes) {
|
||||
mIcon = ContextCompat.getDrawable(SliceBeam.INSTANCE, iconRes);
|
||||
return this;
|
||||
}
|
||||
|
||||
public PreferenceItem setIcon(Drawable drawable) {
|
||||
mIcon = drawable;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PreferenceItem setNoTint(boolean noTint) {
|
||||
this.noTint = noTint;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PreferenceItem setTextColorRes(int textColorRes) {
|
||||
this.textColorRes = textColorRes;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PreferenceItem setOnClickListener(View.OnClickListener onClickListener) {
|
||||
this.onClickListener = onClickListener;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PreferenceHolderView onCreateView(Context ctx) {
|
||||
return new PreferenceHolderView(ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindView(PreferenceHolderView view) {
|
||||
view.bind(this);
|
||||
}
|
||||
|
||||
public final static class PreferenceHolderView extends LinearLayout implements IThemeView {
|
||||
private TextView title, subtitle;
|
||||
private ImageView icon;
|
||||
private TextView value;
|
||||
|
||||
public PreferenceHolderView(Context context) {
|
||||
super(context);
|
||||
|
||||
setOrientation(HORIZONTAL);
|
||||
setGravity(Gravity.CENTER_VERTICAL);
|
||||
|
||||
icon = new ImageView(context);
|
||||
icon.setLayoutParams(new LayoutParams(ViewUtils.dp(28), ViewUtils.dp(28)) {{
|
||||
setMarginStart(ViewUtils.dp(4));
|
||||
setMarginEnd(ViewUtils.dp(8));
|
||||
}});
|
||||
addView(icon);
|
||||
|
||||
LinearLayout innerLayout = new LinearLayout(context);
|
||||
innerLayout.setOrientation(VERTICAL);
|
||||
innerLayout.setGravity(Gravity.CENTER_VERTICAL);
|
||||
|
||||
title = new TextView(context);
|
||||
title.setEllipsize(TextUtils.TruncateAt.END);
|
||||
title.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
|
||||
innerLayout.addView(title);
|
||||
|
||||
subtitle = new TextView(context);
|
||||
subtitle.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14);
|
||||
innerLayout.addView(subtitle);
|
||||
|
||||
addView(innerLayout, new LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f) {{
|
||||
setMarginStart(ViewUtils.dp(8));
|
||||
setMarginEnd(ViewUtils.dp(8));
|
||||
}});
|
||||
|
||||
value = new TextView(context);
|
||||
value.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15);
|
||||
value.setPadding(ViewUtils.dp(8), ViewUtils.dp(6), ViewUtils.dp(8), ViewUtils.dp(6));
|
||||
value.setVisibility(GONE);
|
||||
addView(value, new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||
|
||||
int pad = ViewUtils.dp(12);
|
||||
setPadding(pad, pad, pad, pad);
|
||||
setMinimumHeight(ViewUtils.dp(56));
|
||||
setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||
onApplyTheme();
|
||||
}
|
||||
|
||||
void bind(PreferenceItem item) {
|
||||
title.setText(item.mTitle);
|
||||
title.setVisibility(TextUtils.isEmpty(item.mTitle) ? GONE : VISIBLE);
|
||||
|
||||
CharSequence sub = item.mSubtitle != null ? item.mSubtitle.provide() : null;
|
||||
subtitle.setText(sub);
|
||||
subtitle.setVisibility(TextUtils.isEmpty(sub) ? GONE : VISIBLE);
|
||||
|
||||
CharSequence v = item.valueProvider != null ? item.valueProvider.provide() : null;
|
||||
value.setText(v);
|
||||
value.setVisibility(TextUtils.isEmpty(v) ? GONE : VISIBLE);
|
||||
|
||||
if (item.mIcon != null) {
|
||||
icon.setVisibility(VISIBLE);
|
||||
icon.setImageDrawable(item.mIcon);
|
||||
} else {
|
||||
icon.setVisibility(GONE);
|
||||
}
|
||||
if (item.onClickListener != null) {
|
||||
setOnClickListener(item.onClickListener);
|
||||
} else {
|
||||
setClickable(false);
|
||||
}
|
||||
|
||||
if (item.textColorRes != 0) {
|
||||
title.setTextColor(ThemesRepo.getColor(item.textColorRes));
|
||||
}
|
||||
|
||||
if (item.textColorRes != 0 || item.mIcon != null) {
|
||||
title.setTypeface(ViewUtils.getTypeface(ViewUtils.ROBOTO_MEDIUM));
|
||||
} else {
|
||||
title.setTypeface(Typeface.DEFAULT);
|
||||
}
|
||||
|
||||
if (item.noTint) {
|
||||
icon.setImageTintList(null);
|
||||
} else {
|
||||
icon.setImageTintList(ColorStateList.valueOf(ThemesRepo.getColor(item.textColorRes != 0 ? item.textColorRes : android.R.attr.textColorSecondary)));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApplyTheme() {
|
||||
title.setTextColor(ThemesRepo.getColor(android.R.attr.textColorPrimary));
|
||||
subtitle.setTextColor(ThemesRepo.getColor(android.R.attr.textColorSecondary));
|
||||
value.setTextColor(ThemesRepo.getColor(android.R.attr.textColorSecondary));
|
||||
icon.setImageTintList(ColorStateList.valueOf(ThemesRepo.getColor(android.R.attr.textColorSecondary)));
|
||||
setBackground(ViewUtils.createRipple(ThemesRepo.getColor(android.R.attr.colorControlHighlight), 16));
|
||||
}
|
||||
}
|
||||
|
||||
public interface ValueProvider {
|
||||
CharSequence provide();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
package ru.ytkab0bp.slicebeam.recycler;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.text.TextUtils;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Gravity;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import ru.ytkab0bp.slicebeam.SliceBeam;
|
||||
import ru.ytkab0bp.slicebeam.theme.IThemeView;
|
||||
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
|
||||
import ru.ytkab0bp.slicebeam.utils.Prefs;
|
||||
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
|
||||
import ru.ytkab0bp.slicebeam.view.BeamSwitch;
|
||||
|
||||
public class PreferenceSwitchItem extends SimpleRecyclerItem<PreferenceSwitchItem.SwitchPreferenceHolderView> {
|
||||
private Drawable mIcon;
|
||||
private CharSequence mTitle;
|
||||
private CharSequence mSubtitle;
|
||||
private String mKey;
|
||||
private boolean mDefaultValue;
|
||||
private CompoundButton.OnCheckedChangeListener mChangeListener;
|
||||
private ValueProvider valueProvider;
|
||||
|
||||
public PreferenceSwitchItem setValueProvider(ValueProvider valueProvider) {
|
||||
this.valueProvider = valueProvider;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PreferenceSwitchItem setKeyAndDefaultValue(String key, boolean value) {
|
||||
mKey = key;
|
||||
mDefaultValue = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PreferenceSwitchItem setChangeListener(CompoundButton.OnCheckedChangeListener listener) {
|
||||
mChangeListener = listener;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PreferenceSwitchItem setTitle(CharSequence title) {
|
||||
mTitle = title;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PreferenceSwitchItem setSubtitle(CharSequence subtitle) {
|
||||
mSubtitle = subtitle;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PreferenceSwitchItem setIcon(int iconRes) {
|
||||
mIcon = ContextCompat.getDrawable(SliceBeam.INSTANCE, iconRes);
|
||||
return this;
|
||||
}
|
||||
|
||||
public PreferenceSwitchItem setIcon(Drawable drawable) {
|
||||
mIcon = drawable;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SwitchPreferenceHolderView onCreateView(Context ctx) {
|
||||
return new SwitchPreferenceHolderView(ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindView(SwitchPreferenceHolderView view) {
|
||||
view.bind(this);
|
||||
}
|
||||
|
||||
public final static class SwitchPreferenceHolderView extends LinearLayout implements IThemeView {
|
||||
public TextView title, subtitle;
|
||||
public ImageView icon;
|
||||
public BeamSwitch matSwitch;
|
||||
|
||||
public SwitchPreferenceHolderView(Context context) {
|
||||
super(context);
|
||||
|
||||
setOrientation(HORIZONTAL);
|
||||
setGravity(Gravity.CENTER_VERTICAL);
|
||||
|
||||
icon = new ImageView(context);
|
||||
icon.setLayoutParams(new LayoutParams(ViewUtils.dp(28), ViewUtils.dp(28)) {{
|
||||
setMarginEnd(ViewUtils.dp(16));
|
||||
gravity = Gravity.CENTER_VERTICAL;
|
||||
}});
|
||||
|
||||
LinearLayout innerLayout = new LinearLayout(context);
|
||||
innerLayout.setOrientation(VERTICAL);
|
||||
innerLayout.setGravity(Gravity.CENTER_VERTICAL);
|
||||
|
||||
title = new TextView(context);
|
||||
title.setEllipsize(TextUtils.TruncateAt.END);
|
||||
title.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
|
||||
innerLayout.addView(title);
|
||||
|
||||
subtitle = new TextView(context);
|
||||
subtitle.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14);
|
||||
innerLayout.addView(subtitle);
|
||||
|
||||
addView(innerLayout, new LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f) {{
|
||||
setMarginStart(ViewUtils.dp(8));
|
||||
setMarginEnd(ViewUtils.dp(8));
|
||||
gravity = Gravity.CENTER_VERTICAL;
|
||||
}});
|
||||
|
||||
addView(matSwitch = new BeamSwitch(context), new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewUtils.dp(32)));
|
||||
|
||||
int pad = ViewUtils.dp(12);
|
||||
setPadding(pad, pad, pad, pad);
|
||||
setMinimumHeight(ViewUtils.dp(52));
|
||||
onApplyTheme();
|
||||
}
|
||||
|
||||
void bind(PreferenceSwitchItem item) {
|
||||
title.setText(item.mTitle);
|
||||
subtitle.setText(item.mSubtitle);
|
||||
if (TextUtils.isEmpty(item.mSubtitle)) {
|
||||
subtitle.setVisibility(GONE);
|
||||
} else {
|
||||
subtitle.setVisibility(VISIBLE);
|
||||
}
|
||||
|
||||
if (item.mIcon != null) {
|
||||
icon.setVisibility(VISIBLE);
|
||||
icon.setImageDrawable(item.mIcon);
|
||||
} else {
|
||||
icon.setVisibility(GONE);
|
||||
}
|
||||
if (item.mKey != null) {
|
||||
matSwitch.setChecked(Prefs.getPrefs().getBoolean(item.mKey, item.mDefaultValue));
|
||||
} else {
|
||||
matSwitch.setChecked(item.valueProvider.provide());
|
||||
}
|
||||
setOnClickListener(v -> {
|
||||
boolean check;
|
||||
if (item.mKey != null) {
|
||||
check = !Prefs.getPrefs().getBoolean(item.mKey, item.mDefaultValue);
|
||||
Prefs.getPrefs().edit().putBoolean(item.mKey, check).apply();
|
||||
matSwitch.setChecked(check);
|
||||
} else {
|
||||
matSwitch.setChecked(check = !matSwitch.isChecked());
|
||||
}
|
||||
|
||||
if (item.mChangeListener != null) {
|
||||
item.mChangeListener.onCheckedChanged(matSwitch, check);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApplyTheme() {
|
||||
title.setTextColor(ThemesRepo.getColor(android.R.attr.textColorPrimary));
|
||||
subtitle.setTextColor(ThemesRepo.getColor(android.R.attr.textColorSecondary));
|
||||
icon.setImageTintList(ColorStateList.valueOf(ThemesRepo.getColor(android.R.attr.colorAccent)));
|
||||
setBackground(ViewUtils.createRipple(ThemesRepo.getColor(android.R.attr.colorControlHighlight), 16));
|
||||
}
|
||||
}
|
||||
|
||||
public interface ValueProvider {
|
||||
boolean provide();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package ru.ytkab0bp.slicebeam.recycler;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class SimpleRecyclerAdapter extends RecyclerView.Adapter {
|
||||
private Map<Class<?>, Integer> viewType = new HashMap<>();
|
||||
private Map<Integer, SimpleRecyclerItem> viewCreator = new HashMap<>();
|
||||
private int lastType;
|
||||
|
||||
private List<SimpleRecyclerItem> items = new ArrayList<>();
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
return new RecyclerView.ViewHolder(viewCreator.get(viewType).onCreateView(parent.getContext())) {};
|
||||
}
|
||||
|
||||
/** @noinspection unchecked*/
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
|
||||
items.get(position).onBindView(holder.itemView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return items.get(position).hashCode();
|
||||
}
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
public void setItems(List<SimpleRecyclerItem> items) {
|
||||
this.items = items;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
Integer t = viewType.get(items.get(position).getClass());
|
||||
if (t == null) {
|
||||
viewType.put(items.get(position).getClass(), t = lastType++);
|
||||
viewCreator.put(t, items.get(position));
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return items.size();
|
||||
}
|
||||
|
||||
public List<SimpleRecyclerItem> getItems() {
|
||||
return items;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package ru.ytkab0bp.slicebeam.recycler;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
|
||||
public abstract class SimpleRecyclerItem<V extends View> {
|
||||
public abstract V onCreateView(Context ctx);
|
||||
public void onBindView(V view) {}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package ru.ytkab0bp.slicebeam.recycler;
|
||||
|
||||
import android.content.Context;
|
||||
import android.widget.Space;
|
||||
|
||||
public class SpaceItem extends SimpleRecyclerItem<Space> {
|
||||
private int x, y;
|
||||
|
||||
public SpaceItem(int x, int y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Space onCreateView(Context ctx) {
|
||||
return new Space(ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindView(Space view) {
|
||||
view.setMinimumWidth(x);
|
||||
view.setMinimumHeight(y);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package ru.ytkab0bp.slicebeam.recycler;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Gravity;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
|
||||
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
|
||||
|
||||
public class TextHintRecyclerItem extends SimpleRecyclerItem<TextView> {
|
||||
public String title;
|
||||
|
||||
public TextHintRecyclerItem() {}
|
||||
|
||||
public TextHintRecyclerItem(String t) {
|
||||
title = t;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextView onCreateView(Context ctx) {
|
||||
TextView tv = new TextView(ctx);
|
||||
tv.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14);
|
||||
tv.setTextColor(ThemesRepo.getColor(android.R.attr.textColorSecondary));
|
||||
tv.setPadding(ViewUtils.dp(12), ViewUtils.dp(12), ViewUtils.dp(12), ViewUtils.dp(12));
|
||||
tv.setGravity(Gravity.CENTER);
|
||||
tv.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||
return tv;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindView(TextView view) {
|
||||
view.setText(title);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user