ข้อความการหมุนของ Android: ฉันใช้ dynamiclayout.draw (canvas) แต่ไม่เคลื่อนไหวตามเส้นทางและไม่สามารถใช้ canvas.drawTextOnPath (ไดนามิกเลย์เอาต์)


11

ฉันใช้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 เดือนที่แล้ว เพื่อให้ง่ายขึ้น (คลาสน้อยลง, เมธอดน้อยลง, ไม่มีเมธอดที่ไม่ได้ใช้ ฯลฯ ) ที่จริงฉันมีแค่สองชั้นเท่านั้น:

  1. RotatingTextSwitcherซึ่งเป็นวิดเจ็ต XML

  2. และRotatableซึ่งมีอาร์เรย์ของสตริงที่จะหมุน

  3. โครงร่าง. XML ที่มีวิดเจ็ต XML RotatingTextSwitcherเพื่อทดสอบ

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

1
คุณอธิบายปัญหาแล้ว แต่ยังไม่ได้บอกว่าโซลูชันของคุณคาดหมายอะไร คุณต้องการเห็นอะไรเมื่อแถวมีสัญลักษณ์มากเกินไป
azizbekian

1
มันไม่มากเกินไปครับ ฉันเพิ่งผ่านคำถามของคุณและไม่สามารถเข้าใจความต้องการของคุณ ฉันถามเพื่อให้ชัดเจนสำหรับผู้อ่านคนอื่นเช่นกัน ฉันจะคิดถึงปัญหาและจะได้คำตอบถ้าฉันเจอ ขอบคุณ
azizbekian

@azizbekian ฉันได้ก้าวหน้าไปมากในการแก้ไขปัญหาของฉัน ตอนนี้ผมคิดว่าผมรู้ว่าสิ่งที่เป็นความผิดพลาด: DynamicLayoutผมไม่ได้ใช้ ดังนั้นตอนนี้ฉันใช้มัน ... แต่ฉันไม่สามารถหมุนไปตามเส้นทาง ฉันแก้ไขคำถามแล้ว เงินรางวัลยังคงมีอยู่ :-)
JarsOfJam-Scheduler

คำตอบ:


0

ความคิดของฉันกำลังเปลี่ยนอินพุตด้วยข้อ จำกัด ปัญหาของคุณ ... ตัวอย่างเช่นถ้าคุณสามารถเขียน 20 Chars ได้อย่างถูกต้องเปลี่ยนอาร์เรย์สตริงให้เป็นบรรทัดที่มี 20 Chars หรือน้อยกว่า ..

เพิ่มรหัสนี้เพื่อตัวสร้างแบบหมุนได้

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

รหัสตัดสาย

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

ฉันก้าวหน้าไปมากในการแก้ไขปัญหาของฉัน ตอนนี้ผมคิดว่าผมรู้ว่าสิ่งที่เป็นความผิดพลาด: DynamicLayoutผมไม่ได้ใช้ ดังนั้นตอนนี้ฉันใช้มัน ... แต่ฉันไม่สามารถหมุนไปตามเส้นทาง ฉันแก้ไขคำถามแล้ว เงินรางวัลยังคงมีอยู่ :-)
JarsOfJam-Scheduler
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.