Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 46 additions & 5 deletions app/src/main/java/me/chan/texas/TexasViewDemoActivity.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
package me.chan.texas;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.graphics.Color;
import android.graphics.Paint;

import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.util.TypedValue;
Expand All @@ -17,6 +21,7 @@
import android.widget.Toast;

import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat;

Expand All @@ -27,9 +32,11 @@
import me.chan.texas.renderer.TexasView;
import me.chan.texas.renderer.TouchEvent;
import me.chan.texas.renderer.selection.Selection;
import me.chan.texas.renderer.ui.rv.anim.SegmentAnimType;
import me.chan.texas.text.BreakStrategy;
import me.chan.texas.text.Document;
import me.chan.texas.text.Paragraph;
import me.chan.texas.text.Segment;
import me.chan.texas.utils.TexasUtils;

public class TexasViewDemoActivity extends AppCompatActivity {
Expand Down Expand Up @@ -367,6 +374,32 @@ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
renderOption.setTypeface(typeface);
mTexasView.refresh(renderOption);

mTexasView.setSegmentAnimator(new TexasView.SegmentAnimator() {

@Nullable
@Override
protected Animator onCreateAnimator(Segment segment, View itemView, SegmentAnimType type) {
if (segment.getTag() != SegmentAnimType.APPEARANCE || type != SegmentAnimType.APPEARANCE) {
return null;
}

Log.d("chan_debug", "onCreateAnimator: " + type + " -> " + segment);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.play(ObjectAnimator.ofFloat(itemView, "alpha", 0, 1))
.with(ObjectAnimator.ofFloat(itemView, "translationY", -itemView.getHeight(), 0));
animatorSet.setDuration(500);
animatorSet.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationCancel(Animator animation) {
super.onAnimationCancel(animation);
itemView.setAlpha(1);
itemView.setTranslationY(0);
}
});
return animatorSet;
}
});

findViewById(me.chan.texas.debug.R.id.add_content).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Expand All @@ -375,8 +408,10 @@ public void onClick(View v) {
protected Document onRead(TexasOption option, @Nullable Document previousDocument) {
return new Document.Builder(previousDocument)
.addSegment(
0,
Paragraph.Builder.newBuilder(option)
.text("hello world")
.tag(SegmentAnimType.APPEARANCE)
.text("生活就像点菜,饥饿时菜会点得特别多,但吃一阵就会意识到浪费;如果慢条斯理地盘算怎么点菜,别人已经要吃完了。")
.build()
)
.build();
Expand All @@ -385,6 +420,9 @@ protected Document onRead(TexasOption option, @Nullable Document previousDocumen
}
});

Selection.Styles styles = Selection.Styles.create(Color.BLUE, Color.RED);
styles.enableFakeBold();

findViewById(me.chan.texas.debug.R.id.anim).setOnClickListener(v -> {
Selection selection = mTexasView.highlightParagraphs(new ParagraphPredicates() {
@Override
Expand All @@ -401,17 +439,20 @@ public boolean acceptParagraph(@Nullable Object paragraphTag) {
return;
}

int backgroundColor = styles.getBackgroundColor();
int textColor = styles.getTextColor();
float fakeBoldFactor = styles.getFakeBoldFactor();
ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1f);
valueAnimator.setDuration(3000);
valueAnimator.setRepeatCount(3);
selection.startAnimator(valueAnimator, new Selection.SelectionAnimatorListener() {
@RequiresApi(api = Build.VERSION_CODES.O)
@Override
protected void onUpdate(ValueAnimator animation, Selection.Styles styles) {
int backgroundColor = (int) styles.getBackgroundColor();
int textColor = styles.getTextColor();
float v = (float) animation.getAnimatedValue();
styles.setTextColor(Color.argb((int) (255 * v), Color.red(textColor), Color.green(textColor), Color.blue(textColor)));
styles.setBackgroundColor(Color.argb((int) (255 * v), Color.red(backgroundColor), Color.green(backgroundColor), Color.blue(backgroundColor)));
styles.setTextColor(Color.argb((int) (255 * v) /* 按需设置透明度 */, Color.red(textColor), Color.green(textColor), Color.blue(textColor)));
styles.setBackgroundColor(Color.argb((int) (255 * v) /* 按需设置透明度 */, Color.red(backgroundColor), Color.green(backgroundColor), Color.blue(backgroundColor)));
styles.setFakeBoldFactor(fakeBoldFactor * v);
}

@Override
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/res/layout/activity_paragraph.xml
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@
android:id="@+id/anim"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="动画" />
android:text="选中动画" />
</LinearLayout>

<FrameLayout
Expand Down
4 changes: 4 additions & 0 deletions library/src/main/java/me/chan/texas/renderer/Renderer.java
Original file line number Diff line number Diff line change
Expand Up @@ -546,4 +546,8 @@ public void smoothScrollBy(int dx, int dy) {
public SelectionMethod getSelectionMethod() {
return mSelectionMethod;
}

public void setSegmentAnimator(TexasView.SegmentAnimator segmentAnimator) {
mRecyclerView.setSegmentAnimator(segmentAnimator);
}
}
28 changes: 28 additions & 0 deletions library/src/main/java/me/chan/texas/renderer/TexasView.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package me.chan.texas.renderer;

import android.animation.Animator;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Resources;
Expand All @@ -15,6 +16,7 @@
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.View;
import android.widget.FrameLayout;

import androidx.annotation.AnyThread;
Expand Down Expand Up @@ -45,6 +47,7 @@
import me.chan.texas.misc.PaintSet;
import me.chan.texas.renderer.core.worker.LoadingWorker;
import me.chan.texas.renderer.selection.Selection;
import me.chan.texas.renderer.ui.rv.anim.SegmentAnimType;
import me.chan.texas.source.Source;
import me.chan.texas.text.BreakStrategy;
import me.chan.texas.text.Document;
Expand Down Expand Up @@ -342,6 +345,14 @@ public void setSegmentDecoration(@NonNull SegmentDecoration segmentDecoration) {
mRenderer.setSegmentDecoration(segmentDecoration);
}

public void setSegmentAnimator(@NonNull SegmentAnimator segmentAnimator) {
if (mRenderer == null) {
return;
}

mRenderer.setSegmentAnimator(segmentAnimator);
}

private void load(String reason) {
if (mRenderer == null) {
return;
Expand Down Expand Up @@ -1046,6 +1057,23 @@ public interface SegmentDecoration {
void onDecorateSegment(int index, int count, Segment segment, Document document, Rect outRect);
}

public abstract static class SegmentAnimator {

public final Animator createAnimator(Segment segment, View itemView, SegmentAnimType type) {
return onCreateAnimator(segment, itemView, type);
}

/**
* @param segment segment
* @param itemView itemView
* @param type type
* @return Animator, 返回空则代表不显示动画
*/
@Nullable
protected abstract Animator onCreateAnimator(Segment segment, View itemView, SegmentAnimType type);
}


/**
* Scroll state listener
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import static androidx.annotation.RestrictTo.Scope.LIBRARY;

import android.graphics.Paint;

import androidx.annotation.MainThread;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
Expand Down Expand Up @@ -342,6 +344,7 @@ private static class InternalSelectionStyle extends TextStyle {
private int mTextColor = 0;
private Selection.Styles mStyles;
private int mBackgroundColor = 0;
private float mFakeBoldFactor = 0f;

public void reset(Selection.Styles styles) {
mStyles = styles;
Expand All @@ -350,11 +353,16 @@ public void reset(Selection.Styles styles) {
public void update() {
mTextColor = mStyles.getTextColor();
mBackgroundColor = mStyles.getBackgroundColor();
mFakeBoldFactor = mStyles.getFakeBoldFactor();
}

@Override
public void update(@NonNull TexasPaint textPaint, @Nullable Object tag) {
textPaint.setColor(mTextColor);
if (mFakeBoldFactor > 0f) {
textPaint.setStyle(Paint.Style.FILL_AND_STROKE);
textPaint.setStrokeWidth(textPaint.getTextSize() * mFakeBoldFactor);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,7 @@ public static Selection obtain(Type type, @Nullable TexasRecyclerView container,
public static class Styles {
private int mBackgroundColor;
private int mTextColor;
private float mFakeBoldFactor = 0f;

private Source mSource;

Expand Down Expand Up @@ -424,6 +425,21 @@ public void setTextColor(int textColor) {
mTextColor = textColor;
}

public void enableFakeBold() {
setFakeBoldFactor(0.05f);
}

public void setFakeBoldFactor(float fakeBoldFactor) {
if (mFakeBoldFactor != fakeBoldFactor) {
++mVersion;
}
mFakeBoldFactor = fakeBoldFactor;
}

public float getFakeBoldFactor() {
return mFakeBoldFactor;
}

@Override
public String toString() {
return "Styles{" +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@
import me.chan.texas.utils.TexasUtils;

import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
import androidx.recyclerview.widget.RecyclerView;

@RestrictTo(RestrictTo.Scope.LIBRARY)
public class SegmentItemDecoration extends RecyclerView.ItemDecoration {
private final TexasRendererAdapter mAdapter;
private final me.chan.texas.misc.Rect mRect = new me.chan.texas.misc.Rect();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,18 @@
import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.SimpleItemAnimator;

import static androidx.annotation.RestrictTo.Scope.LIBRARY;

import me.chan.texas.R;
import me.chan.texas.misc.Rect;
import me.chan.texas.renderer.TexasView;
import me.chan.texas.renderer.TouchEvent;
import me.chan.texas.renderer.ui.TexasRendererAdapter;
import me.chan.texas.renderer.ui.rv.anim.DefaultTexasItemAnimator;
import me.chan.texas.renderer.ui.text.ParagraphView;
import me.chan.texas.text.Document;
import me.chan.texas.text.Paragraph;
import me.chan.texas.text.Segment;
import me.chan.texas.renderer.selection.SelectionProvider;
import me.chan.texas.text.ViewSegment;
import me.chan.texas.text.layout.Layout;

Expand All @@ -31,6 +30,7 @@ public class TexasRecyclerViewImpl extends RecyclerView implements TexasRecycler
private OnClickedListener mOnClickedListener;
private ScrollAction mScrollAction;
private final TexasLinearLayoutManagerImpl mTexasLinearLayoutManager;
private final DefaultTexasItemAnimator mItemAnimator = new DefaultTexasItemAnimator();

public TexasRecyclerViewImpl(@NonNull Context context, TexasLinearLayoutManagerImpl texasLinearLayoutManager) {
super(context);
Expand All @@ -48,12 +48,7 @@ protected void onClicked(MotionEvent event) {
}
};

ItemAnimator itemAnimator = getItemAnimator();
if (itemAnimator instanceof SimpleItemAnimator) {
SimpleItemAnimator simpleItemAnimator = (SimpleItemAnimator) itemAnimator;
simpleItemAnimator.setSupportsChangeAnimations(false);
simpleItemAnimator.setChangeDuration(0);
}
setItemAnimator(mItemAnimator);
}

public void scrollToPosition(int position, boolean smooth, int offset) {
Expand Down Expand Up @@ -86,6 +81,10 @@ public void getChildLocations(View child, Rect locations) {
locations.bottom = child.getBottom();
}

public void setSegmentAnimator(TexasView.SegmentAnimator segmentAnimator) {
mItemAnimator.setSegmentItemAnimator(segmentAnimator);
}

private class ScrollAction implements Runnable {
public int position;
public int offset;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package me.chan.texas.renderer.ui.rv.anim;

import android.animation.Animator;

class AnimRecord {
public static final int PHASE_PENDING = 0;
public static final int PHASE_POSTPONED = 1;
public static final int PHASE_RUNNING = 2;

public final SegmentAnimType type;
public int phase;
public final Animator animator;

AnimRecord(SegmentAnimType type, Animator animator) {
this.type = type;
this.animator = animator;
this.phase = PHASE_PENDING;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package me.chan.texas.renderer.ui.rv.anim;

import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

class AnimTracker {
private final HashMap<RecyclerView.ViewHolder, AnimRecord> mRecords = new HashMap<>();

public void add(RecyclerView.ViewHolder holder, AnimRecord record) {
mRecords.put(holder, record);
}

public boolean remove(RecyclerView.ViewHolder holder) {
return mRecords.remove(holder) != null;
}

@Nullable
public AnimRecord get(RecyclerView.ViewHolder holder) {
return mRecords.get(holder);
}

public void advanceTo(RecyclerView.ViewHolder holder, int phase) {
AnimRecord record = mRecords.get(holder);
if (record != null) {
record.phase = phase;
}
}

public List<RecyclerView.ViewHolder> holdersByPhase(int phase) {
List<RecyclerView.ViewHolder> result = new ArrayList<>();
for (HashMap.Entry<RecyclerView.ViewHolder, AnimRecord> entry : mRecords.entrySet()) {
if (entry.getValue().phase == phase) {
result.add(entry.getKey());
}
}
return result;
}

public List<RecyclerView.ViewHolder> allHolders() {
return new ArrayList<>(mRecords.keySet());
}

public boolean isEmpty() {
return mRecords.isEmpty();
}
}
Loading