ฉันใช้https://github.com/mdg-iitr/RotatingTextเวอร์ชันของตัวเองเพื่อแสดงวิดเจ็ตข้อความหมุน วิดีโอมีให้ใน GitHub นี้ช่วยให้คุณดูภาพเคลื่อนไหว ความคิดคือการตั้งค่าแถวของคำ แถวจะแสดงแถวหลังแถว แถวทั้งหมดหมุน (ทำตามคำพูด) แถวจะปรากฏขึ้นหลังจากแถวก่อนหน้าเมื่อภาพเคลื่อนไหวการหมุนของหลังสิ้นสุดลง
ปัญหาของฉัน
ฉันใช้ a DynamicLayout
เพื่อแสดงแถวของข้อความ ข้อควรจำ: แถวจะต้องหมุน
ปัญหาของฉันคือ: canvas.drawTextOnPath(dynamicLayoutObject)
เห็นได้ชัดว่าผมไม่สามารถใช้วิธีการ ดังนั้นสิ่งที่ฉันทำคือ: dynamicLayoutObjec.draw(canvas);
. แต่ตอนนั้นไม่มีแอนิเมชันใด ๆ แท้จริงแล้วข้อความ (ดังนั้นDynamicLayout
ที่มีอยู่) จะต้องหมุน
ผลลัพธ์ที่คาดหวัง
DynamicLayout
(ในความเป็นจริงข้อความ) จะต้องเป็นภาพเคลื่อนไหว (หมุน) การหมุนสามารถพบได้ในภาพประกอบของ repo ต้นฉบับ Github ที่กำหนดไว้ในตอนต้นของคำถาม SO นี้ ( https://github.com/mdg-iitr/RotatingText )
คำถามของฉัน
ฉันไม่ทราบวิธีที่จะทำให้DynamicLayout
(และ / หรือข้อความ) หมุนไปตามเส้นทางของฉัน
ตัวอย่างที่น้อยที่สุดและทดสอบได้
ฉันได้แก้ไขห้องสมุด RotatingText ดั้งเดิมเมื่อ 8 เดือนที่แล้ว เพื่อให้ง่ายขึ้น (คลาสน้อยลง, เมธอดน้อยลง, ไม่มีเมธอดที่ไม่ได้ใช้ ฯลฯ ) ที่จริงฉันมีแค่สองชั้นเท่านั้น:
RotatingTextSwitcher
ซึ่งเป็นวิดเจ็ต XMLและ
Rotatable
ซึ่งมีอาร์เรย์ของสตริงที่จะหมุนโครงร่าง. XML ที่มีวิดเจ็ต XML
RotatingTextSwitcher
เพื่อทดสอบFragment
พองรูปแบบ mentionned ก่อนหน้านี้, การตั้งค่าคำพูดของแต่ละแถวหมุนและแสดงให้พวกเขา
ในการทดสอบให้สร้างกิจกรรมที่แสดงชิ้นส่วนที่ระบุด้านล่างซึ่งจะใช้แหล่งข้อมูลอื่นที่นำเสนอข้างต้น
คลาสที่หมุนได้
import android.graphics.Path;
import android.view.animation.Interpolator;
public class Rotatable {
private final String[] text;
private final int update_duration;
private int animation_duration;
private Path path_in, path_out;
private int currentWordNumber;
private Interpolator interpolator;
public Rotatable(int update_duration, int animation_duration, Interpolator interpolator, String... text) {
this.update_duration = update_duration;
this.animation_duration = animation_duration;
this.text = text;
this.interpolator = interpolator;
currentWordNumber = -1;
}
private int nextWordNumber() {
currentWordNumber = (currentWordNumber + 1) % text.length;
return currentWordNumber;
}
String nextWord() {
return text[nextWordNumber()];
}
Path getPathIn() {
return path_in;
}
void setPathIn(Path path_in) {
this.path_in = path_in;
}
Path getPathOut() {
return path_out;
}
void setPathOut(Path path_out) {
this.path_out = path_out;
}
int getUpdateDuration() {
return update_duration;
}
int getAnimationDuration() {
return animation_duration;
}
Interpolator getInterpolator() { return interpolator; }
}
คลาส RotatingTextSwitcher
package libs.rotating_text;
import android.animation.ValueAnimator;
import android.app.Activity;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.text.DynamicLayout;
import android.text.Layout;
import android.text.SpannableStringBuilder;
import android.text.StaticLayout;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import androidx.appcompat.widget.AppCompatTextView;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Consumer;
import io.reactivex.schedulers.Schedulers;
import androidx.annotation.Nullable;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.TimeUnit;
public class RotatingTextSwitcher extends AppCompatTextView {
Disposable disposable;
private TextPaint textPaint = new TextPaint();
private String text = "", old_text = "";
SpannableStringBuilder base = new SpannableStringBuilder(text);
SpannableStringBuilder base_old = new SpannableStringBuilder(old_text);
private DynamicLayout layout = new DynamicLayout(base, textPaint,500, Layout.Alignment.ALIGN_CENTER,1.0F,0.0F,true);
private DynamicLayout layout_old = new DynamicLayout(base_old, textPaint,500, Layout.Alignment.ALIGN_CENTER,1.0F,0.0F,true);
private Rotatable rotatable;
private Paint paint;
private Path path_in, path_out;
public RotatingTextSwitcher(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
paint = getPaint();
paint.setAntiAlias(true);
}
public void setRotatable(Rotatable rotatable) {
this.rotatable = rotatable;
initialize();
}
private void initialize() {
text = rotatable.nextWord();
base.clear();
base.append(text);
old_text = text;
base_old.clear();
base_old.append(old_text);
setUpPath();
setDisposable();
scheduleUpdateTextTimer();
}
private void setDisposable() {
disposable = Observable.interval(1000 / 60, TimeUnit.MILLISECONDS, Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<Long>() {
@Override
public void accept(Long aLong) {
invalidate();
}
});
}
private void setUpPath() {
post(new Runnable() {
@Override
public void run() {
path_in = new Path();
path_in.moveTo(0.0f, getHeight() - paint.getFontMetrics().bottom);
path_in.lineTo(getWidth(), getHeight() - paint.getFontMetrics().bottom);
rotatable.setPathIn(path_in);
path_out = new Path();
path_out.moveTo(0.0f, (2 * getHeight()) - paint.getFontMetrics().bottom);
path_out.lineTo(getWidth(), (2 * getHeight()) - paint.getFontMetrics().bottom);
rotatable.setPathOut(path_out);
}
});
}
private void scheduleUpdateTextTimer() {
Timer update_text_timer = new Timer();
update_text_timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
((Activity) getContext()).runOnUiThread(new Runnable() {
@Override
public void run() {
animateInHorizontal();
animateOutHorizontal();
old_text = text;
base_old.clear();
base_old.append(old_text);
text = rotatable.nextWord();
base.clear();
base.append(text);
}
});
}
}, rotatable.getUpdateDuration(), rotatable.getUpdateDuration());
}
@Override
protected void onDraw(Canvas canvas) {
DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();
float size = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 32, metrics);
textPaint.setTextSize(size);
if (rotatable.getPathIn() != null) {
layout.draw(canvas);
//canvas.drawTextOnPath(text, rotatable.getPathIn(), 0.0f, 0.0f, paint);
}
if (rotatable.getPathOut() != null) {
layout_old.draw(canvas);
//canvas.drawTextOnPath(old_text, rotatable.getPathOut(), 0.0f, 0.0f, paint);
}
setHeight(layout.getHeight() + layout_old.getHeight());
}
private void animateInHorizontal() {
ValueAnimator animator = ValueAnimator.ofFloat(0.0f, getHeight());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
path_in = new Path();
path_in.moveTo(0.0f, (Float) valueAnimator.getAnimatedValue() - paint.getFontMetrics().bottom);
path_in.lineTo(getWidth(), (Float) valueAnimator.getAnimatedValue() - paint.getFontMetrics().bottom);
rotatable.setPathIn(path_in);
}
});
animator.setInterpolator(rotatable.getInterpolator());
animator.setDuration(rotatable.getAnimationDuration());
animator.start();
}
private void animateOutHorizontal() {
ValueAnimator animator = ValueAnimator.ofFloat(getHeight(), getHeight() * 2.0f);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
path_out = new Path();
path_out.moveTo(0.0f, (Float) valueAnimator.getAnimatedValue() - paint.getFontMetrics().bottom);
path_out.lineTo(getWidth(), (Float) valueAnimator.getAnimatedValue() - paint.getFontMetrics().bottom);
rotatable.setPathOut(path_out);
}
});
animator.setInterpolator(rotatable.getInterpolator());
animator.setDuration(rotatable.getAnimationDuration());
animator.start();
}
}
รูปแบบ
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<libs.rotating_text.RotatingTextSwitcher
android:id="@+id/textView_presentation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="50dp"
android:textSize="35sp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
ส่วน
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateInterpolator;
import android.widget.ImageView;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.example.androidframework.R;
import libs.rotating_text.Rotatable;
import libs.rotating_text.RotatingTextSwitcher;
public class FragmentHomeSlide extends Fragment {
private View inflated;
private int drawable_id;
private String[] text_presentation;
@Override
public void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
assert getArguments() != null;
text_presentation = new String[];
text_presentation[0] = "One row is set up with several words";
text_presentation[1] = "This is another row";
}
@Override
public View onCreateView(final LayoutInflater inflater, @Nullable final ViewGroup container, @Nullable final Bundle savedInstanceState) {
inflated = inflater.inflate(R.layout.home_slide, container, false);
setWidgets();
return inflated;
}
private void setWidgets() {
final RotatingTextSwitcher rotating_presentation = inflated.findViewById(R.id.textView_presentation);
rotating_presentation.setRotatable(new Rotatable(1000, 500, new AccelerateInterpolator(), text_presentation));
}
}
DynamicLayout
ผมไม่ได้ใช้ ดังนั้นตอนนี้ฉันใช้มัน ... แต่ฉันไม่สามารถหมุนไปตามเส้นทาง ฉันแก้ไขคำถามแล้ว เงินรางวัลยังคงมีอยู่ :-)