Teks berputar Android: Saya menggunakan dynamiclayout.draw (kanvas) tetapi tidak bernyawa di sepanjang jalan, dan tidak dapat menggunakan canvas.drawTextOnPath (dynamiclayout)


11

Saya menggunakan versi saya sendiri https://github.com/mdg-iitr/RotatingText untuk menampilkan widget teks yang berputar. Video tersedia di GitHub ini, memungkinkan Anda untuk melihat animasi. Idenya adalah mengatur barisan kata-kata. Baris ditampilkan baris demi baris. Seluruh baris berputar (begitu juga kata-katanya). Baris ditampilkan setelah baris sebelumnya ketika animasi rotasi yang terakhir telah berakhir.

Masalahku

Saya menggunakan a DynamicLayoutuntuk menunjukkan baris teks. Ingat: baris harus berputar.

Masalah saya adalah: jelas, saya tidak bisa menggunakan metode ini canvas.drawTextOnPath(dynamicLayoutObject). Jadi apa yang saya lakukan adalah: dynamicLayoutObjec.draw(canvas);. Tapi saat itu belum ada animasi. Memang, teks (jadi DynamicLayoutyang berisi itu) harus diputar.

Hasil yang diharapkan

The DynamicLayout(pada kenyataannya, teks-nya) harus animasi (rotasi). Rotasi dapat ditemukan dalam ilustrasi repo asli Github yang diberikan pada awal Pertanyaan SO ini ( https://github.com/mdg-iitr/RotatingText ).

Pertanyaan saya

Saya tidak tahu cara membuat DynamicLayout(dan / atau teksnya) saya berputar di sepanjang jalur saya.

Contoh Minimal dan Dapat Diuji

Saya telah memodifikasi perpustakaan asli RotatingText 8 bulan yang lalu sekitar. untuk menyederhanakannya (lebih sedikit kelas, lebih sedikit metode, tidak ada metode yang tidak digunakan, dll.). Memang, saya hanya punya dua kelas:

  1. RotatingTextSwitcher, yang merupakan widget XML

  2. Dan Rotatable, yang berisi deretan string yang akan diputar.

  3. Tata letak .XML yang berisi widget XML RotatingTextSwitcheruntuk mengujinya

  4. A Fragmentmenggembungkan tata letak yang disebutkan sebelumnya, mengatur kata-kata dari setiap baris yang berputar, dan menunjukkannya.

Untuk mengujinya, buat aktivitas yang menunjukkan fragmen yang diberikan di bawah ini, yang pada gilirannya menggunakan sumber lain, yang disajikan di atas.

Kelas yang dapat diputar

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

Kelas 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();
    }


}

Tata letak

<?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>

Sebuah Fragmen

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

1
Anda telah menggambarkan masalahnya, tetapi belum memberi tahu apa solusi yang Anda harapkan. Apa yang ingin Anda lihat ketika baris memiliki terlalu banyak simbol?
azizbekian

1
tidak terlalu banyak, tuan. Saya baru saja menjawab pertanyaan Anda dan tidak dapat memahami kebutuhan Anda. Saya memintanya untuk menjelaskan kepada pembaca lain juga. Saya akan memikirkan masalahnya dan akan memberikan jawaban jika saya menemukan beberapa. Terima kasih.
azizbekian

@azizbekian Saya telah mengalami banyak kemajuan dalam penyelesaian masalah saya. Sekarang saya pikir saya tahu apa yang salah: saya tidak menggunakannya DynamicLayout. Jadi, sekarang saya menggunakannya ... tapi saya tidak bisa membuatnya berputar di sepanjang jalan. Saya telah mengedit pertanyaan. Hadiah masih tersedia :-).
JarsOfJam-Scheduler

Jawaban:


0

ide saya adalah mengubah input dengan batas masalah Anda ... misalnya jika Anda dapat menulis 20 Chars dengan benar mengubah array string ke baris dengan 20 Chars atau kurang ..

tambahkan kode ini ke konstruktor yang dapat diputar

    ArrayList<String> newttext = new ArrayList<>();
    for (int i = 0; i < text.length; i++) {
        if (text[i].length() > 20){ // 20 is number characters
            ArrayList<String> strings = myCutter(text[i]);
            newttext.addAll(strings);
        }else{
            newttext.add(text[i]);
        }
    }

Kode Pemotong baris

    public ArrayList<String> myCutter(String s) {
    String trueLine = "";
    ArrayList<String> result = new ArrayList<>();
    if (s.length() > 20) {
        String[] split = s.split(" ");
        for (int i = 0; i < split.length; i++) {
            if (trueLine.length() + split[i].length() < 20) {
                trueLine = trueLine + " " + split[i];
            } else {
                result.add(trueLine);
                trueLine = "";
            }
        }
    } else {
        result.add(s);
    }
    return result;
}

Saya telah mengalami banyak kemajuan dalam penyelesaian masalah saya. Sekarang saya pikir saya tahu apa yang salah: saya tidak menggunakannya DynamicLayout. Jadi, sekarang saya menggunakannya ... tapi saya tidak bisa membuatnya berputar di sepanjang jalan. Saya telah mengedit pertanyaan. Hadiah masih tersedia :-).
JarsOfJam-Scheduler
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.