如何更改 actionPerformed() 内的 Swing Timer Delay

2024-03-15

所以我正在构建这个音乐播放器应用程序,它可以播放拖放到 JLabel 上的音符。当我按下播放按钮时,我希望每个音符都突出显示,并带有与该音符对应的延迟值。我为此使用了 Swing Timer,但问题是,它只是以构造函数中指定的恒定延迟循环。

playButton.addActionListener(e -> {
        timerI = 0;
        System.out.println("Entered onAction");

        Timer t = new Timer(1000, e1 -> {
            if (timerI < 24) {
                NoteLabel thisNote = (NoteLabel)staff.getComponent(timerI);
                NoteIcon thisIcon = thisNote.getIcon();
                String noteName = thisIcon.getNoteName();
                thisNote.setIcon(noteMap.get(noteName + "S"));
                timerI++;
            }
        });
        t.start();
    });

它有效,但我想让计时器延迟动态化。每个NoteIcon对象有一个保存延迟值的属性,我希望计时器等待不同的时间,具体取决于哪个NoteIcon在该循环中获取。 (第一个循环退出 1 秒,然后是 2、4、1 等) 我该怎么做呢?


Caveats:

  • 动画并不简单。情况很复杂。它有许多重要的理论,旨在使动画看起来更好
  • 好的动画很难
  • 动画是illusion随着时间的推移而变化的
  • 我所展示的大部分内容都是基于库代码,因此会稍微复杂一些,但是是为重用和抽象而设计的

理论 tl;dr

好吧,一些非常无聊的理论。但首先,我不打算谈论的事情 - 缓动或动画曲线。这些会改变动画在给定时间内播放的速度,使动画看起来更自然,但我可以用整个答案来谈论其他内容:/

您要做的第一件事就是抽象您的概念。例如。动画通常是随时间变化的(有些动画在无限长的时间内是线性的,但是,让我们尝试将其保持在问题的范围内)。

因此,我们立刻就有了两个重要的概念。第一个是持续时间,第二个是在该持续时间内从 A 点到 B 点的标准化进度。也就是说,在持续时间的一半时,进展将是0.5。这很重要,因为它允许我们抽象概念并使框架动态化。

动画太快?更改持续时间,其他一切保持不变。

一个时间线...

好吧,音乐是一条时间线。它有一个定义的起点和终点(再次,保持简单)以及沿着该时间线“做事”的事件,独立于音乐时间线(即,每个音符可以播放指定的持续时间,独立于音乐时间线,这将继续前进甚至完成)

首先,我们需要一个注释...

public class Note {
    private Duration duration;

    public Note(Duration duration) {
        this.duration = duration;
    }

    public Duration getDuration() {
        return duration;
    }
}

以及基于“事件”的时间线,它描述了在正常时间段内何时应该播放这些音符。

public static class EventTimeLine<T> {

    private Map<Double, KeyFrame<T>> mapEvents;

    public EventTimeLine() {
        mapEvents = new TreeMap<>();
    }

    public void add(double progress, T value) {
        mapEvents.put(progress, new KeyFrame<T>(progress, value));
    }

    public List<T> getValues() {
        return Collections.unmodifiableList(mapEvents.values().stream()
                .map(kf -> kf.getValue())
                .collect(Collectors.toList()));
    }

    public double getPointOnTimeLineFor(T value) {
        for (Map.Entry<Double, KeyFrame<T>> entry : mapEvents.entrySet()) {
            if (entry.getValue().getValue() == value) {
                return entry.getKey();
            }
        }

        return -1;
    }

    public List<T> getValuesAt(double progress) {

        if (progress < 0) {
            progress = 0;
        } else if (progress > 1) {
            progress = 1;
        }

        return getKeyFramesBetween(progress, 0.01f)
                .stream()
                .map(kf -> kf.getValue())
                .collect(Collectors.toList());
    }

    public List<KeyFrame<T>> getKeyFramesBetween(double progress, double delta) {

        int startAt = 0;

        List<Double> keyFrames = new ArrayList<>(mapEvents.keySet());
        while (startAt < keyFrames.size() && keyFrames.get(startAt) <= progress - delta) {
            startAt++;
        }

        startAt = Math.min(keyFrames.size() - 1, startAt);
        int endAt = startAt;
        while (endAt < keyFrames.size() && keyFrames.get(endAt) <= progress + delta) {
            endAt++;
        }
        endAt = Math.min(keyFrames.size() - 1, endAt);

        List<KeyFrame<T>> frames = new ArrayList<>(5);
        for (int index = startAt; index <= endAt; index++) {
            KeyFrame<T> keyFrame = mapEvents.get(keyFrames.get(index));
            if (keyFrame.getProgress() >= progress - delta
                    && keyFrame.getProgress() <= progress + delta) {
                frames.add(keyFrame);
            }
        }

        return frames;

    }

    public class KeyFrame<T> {

        private double progress;
        private T value;

        public KeyFrame(double progress, T value) {
            this.progress = progress;
            this.value = value;
        }

        public double getProgress() {
            return progress;
        }

        public T getValue() {
            return value;
        }

        @Override
        public String toString() {
            return "KeyFrame progress = " + getProgress() + "; value = " + getValue();
        }

    }

}

然后你可以创建一个音乐时间线,比如......

musicTimeLine = new EventTimeLine<Note>();
musicTimeLine.add(0.1f, new Note(Duration.ofMillis(1000)));
musicTimeLine.add(0.12f, new Note(Duration.ofMillis(500)));
musicTimeLine.add(0.2f, new Note(Duration.ofMillis(500)));
musicTimeLine.add(0.21f, new Note(Duration.ofMillis(500)));
musicTimeLine.add(0.22f, new Note(Duration.ofMillis(500)));
musicTimeLine.add(0.25f, new Note(Duration.ofMillis(1000)));
musicTimeLine.add(0.4f, new Note(Duration.ofMillis(2000)));
musicTimeLine.add(0.5f, new Note(Duration.ofMillis(2000)));
musicTimeLine.add(0.7f, new Note(Duration.ofMillis(2000)));
musicTimeLine.add(0.8f, new Note(Duration.ofMillis(2000)));

请注意,这里我将笔记定义为以固定持续时间运行。你“可以”让它们按照时间线持续时间的百分比播放......但只是说这很难,所以我将把它留给你;)

动画引擎

所呈现的(简单)动画引擎使用单个Timer,高速运转,作为中央“滴答”发动机。

然后它通知Animatable实际执行底层动画的对象。

通常,我会在一系列值(从 - 到)上进行动画处理,但在本例中,我们实际上只对动画播放的时间量感兴趣。由此我们可以确定应该演奏哪些音符并对音符进行动画处理,在本例中,更改 alpha 值,但您同样可以更改表示音符的对象的大小,但这将是不同的Animatable实现,我在这里没有介绍。

如果你有兴趣的话我的SuperSimpleSwing动画框架 https://github.com/RustyKnight/SuperSimpleSwingAnimationFramework,这个例子是松散地基于的,包含基于“范围”的Animatables ...有趣的东西。

在该示例中,一个Animatable用于驱动音乐EventTimeLine,它只是检查时间线中是否有需要在特定时间点播放的任何“音符”。

一秒BlendingTimeLine用于控制alpha值(0-1-0)。然后为每个注释提供它自己的Animatable它驱动此混合时间线,并使用其值来动画突出显示音符的 alpha 变化。

这是 API 解耦性质的一个很好的例子 -BlendingTimeLine用于所有笔记。这Animatable只需获取他们玩过的时间,从时间线中提取所需的值并应用即可。

这意味着每个音符仅在其自己指定的持续时间内突出显示,所有这些都是独立的。

可运行的示例...

注:如果我这样做,我会将解决方案抽象到更高的水平

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.stream.Collectors;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private EventTimeLine<Note> musicTimeLine;
        private DefaultDurationAnimatable timeLineAnimatable;

        private Double playProgress;

        private Set<Note> playing = new HashSet<Note>(5);
        private Map<Note, Double> noteAlpha = new HashMap<>(5);

        private DoubleBlender blender = new DoubleBlender();
        private BlendingTimeLine<Double> alphaTimeLine = new BlendingTimeLine<>(blender);

        public TestPane() {
            musicTimeLine = new EventTimeLine<Note>();
            musicTimeLine.add(0.1f, new Note(Duration.ofMillis(1000)));
            musicTimeLine.add(0.12f, new Note(Duration.ofMillis(500)));
            musicTimeLine.add(0.2f, new Note(Duration.ofMillis(500)));
            musicTimeLine.add(0.21f, new Note(Duration.ofMillis(500)));
            musicTimeLine.add(0.22f, new Note(Duration.ofMillis(500)));
            musicTimeLine.add(0.25f, new Note(Duration.ofMillis(1000)));
            musicTimeLine.add(0.4f, new Note(Duration.ofMillis(2000)));
            musicTimeLine.add(0.5f, new Note(Duration.ofMillis(2000)));
            musicTimeLine.add(0.7f, new Note(Duration.ofMillis(2000)));
            musicTimeLine.add(0.8f, new Note(Duration.ofMillis(2000)));

            alphaTimeLine.add(0.0f, 0.0);
            alphaTimeLine.add(0.5f, 1.0);
            alphaTimeLine.add(1.0f, 0.0);

            timeLineAnimatable = new DefaultDurationAnimatable(Duration.ofSeconds(10),
                    new AnimatableListener() {
                @Override
                public void animationChanged(Animatable animator) {
                    double progress = timeLineAnimatable.getPlayedDuration();
                    playProgress = progress;
                    List<Note> notes = musicTimeLine.getValuesAt(progress);
                    if (notes.size() > 0) {
                        System.out.println(">> " + progress + " @ " + notes.size());
                        for (Note note : notes) {
                            playNote(note);
                        }
                    }
                    repaint();
                }
            }, null);

            timeLineAnimatable.start();
        }

        protected void playNote(Note note) {
            // Note is already playing...
            // Equally, we could maintain a reference to the animator, mapped to
            // the note, but what ever...
            if (playing.contains(note)) {
                return;
            }
            playing.add(note);

            DurationAnimatable noteAnimatable = new DefaultDurationAnimatable(note.getDuration(), new AnimatableListener() {
                @Override
                public void animationChanged(Animatable animator) {
                    DurationAnimatable da = (DurationAnimatable) animator;
                    double progress = da.getPlayedDuration();
                    double alpha = alphaTimeLine.getValueAt((float) progress);
                    noteAlpha.put(note, alpha);
                    repaint();
                }
            }, new AnimatableLifeCycleListenerAdapter() {
                @Override
                public void animationCompleted(Animatable animator) {
                    playing.remove(note);
                    noteAlpha.remove(note);
                    repaint();
                }
            });
            noteAnimatable.start();
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(200, 100);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();

            int startX = 10;
            int endX = getWidth() - 10;
            int range = endX - startX;

            int yPos = getHeight() / 2;

            g2d.setColor(Color.DARK_GRAY);
            g2d.drawLine(startX, yPos, endX, yPos);

            List<Note> notes = musicTimeLine.getValues();
            for (Note note : notes) {
                double potl = musicTimeLine.getPointOnTimeLineFor(note);
                double xPos = startX + (range * potl);
                // Technically, this could be cached...
                Ellipse2D notePoint = new Ellipse2D.Double(xPos - 2.5, yPos - 2.5, 5, 5);
                g2d.fill(notePoint);

                if (noteAlpha.containsKey(note)) {
                    double alpha = noteAlpha.get(note);
                    // I'm lazy :/
                    // It's just simpler to copy the current context, modify the
                    // composite, paint and then dispose of, then trying to 
                    // track and reset the composite manually
                    Graphics2D alpha2d = (Graphics2D) g2d.create();
                    alpha2d.setComposite(AlphaComposite.SrcOver.derive((float) alpha));
                    Ellipse2D playedNote = new Ellipse2D.Double(xPos - 5, yPos - 5, 10, 10);
                    alpha2d.setColor(Color.RED);
                    alpha2d.fill(playedNote);
                    alpha2d.dispose();
                }
            }

            double playXPos = startX + (range * playProgress);
            g2d.setColor(Color.RED);
            Line2D playLine = new Line2D.Double(playXPos, 0, playXPos, getHeight());
            g2d.draw(playLine);

            g2d.dispose();
        }

    }

    public class Note {

        private Duration duration;

        public Note(Duration duration) {
            this.duration = duration;
        }

        public Duration getDuration() {
            return duration;
        }
    }

    public static class EventTimeLine<T> {

        private Map<Double, KeyFrame<T>> mapEvents;

        public EventTimeLine() {
            mapEvents = new TreeMap<>();
        }

        public void add(double progress, T value) {
            mapEvents.put(progress, new KeyFrame<T>(progress, value));
        }

        public List<T> getValues() {
            return Collections.unmodifiableList(mapEvents.values().stream()
                    .map(kf -> kf.getValue())
                    .collect(Collectors.toList()));
        }

        public double getPointOnTimeLineFor(T value) {
            for (Map.Entry<Double, KeyFrame<T>> entry : mapEvents.entrySet()) {
                if (entry.getValue().getValue() == value) {
                    return entry.getKey();
                }
            }

            return -1;
        }

        public List<T> getValuesAt(double progress) {

            if (progress < 0) {
                progress = 0;
            } else if (progress > 1) {
                progress = 1;
            }

            return getKeyFramesBetween(progress, 0.01f)
                    .stream()
                    .map(kf -> kf.getValue())
                    .collect(Collectors.toList());
        }

        public List<KeyFrame<T>> getKeyFramesBetween(double progress, double delta) {

            int startAt = 0;

            List<Double> keyFrames = new ArrayList<>(mapEvents.keySet());
            while (startAt < keyFrames.size() && keyFrames.get(startAt) <= progress - delta) {
                startAt++;
            }

            startAt = Math.min(keyFrames.size() - 1, startAt);
            int endAt = startAt;
            while (endAt < keyFrames.size() && keyFrames.get(endAt) <= progress + delta) {
                endAt++;
            }
            endAt = Math.min(keyFrames.size() - 1, endAt);

            List<KeyFrame<T>> frames = new ArrayList<>(5);
            for (int index = startAt; index <= endAt; index++) {
                KeyFrame<T> keyFrame = mapEvents.get(keyFrames.get(index));
                if (keyFrame.getProgress() >= progress - delta
                        && keyFrame.getProgress() <= progress + delta) {
                    frames.add(keyFrame);
                }
            }

            return frames;

        }

        public class KeyFrame<T> {

            private double progress;
            private T value;

            public KeyFrame(double progress, T value) {
                this.progress = progress;
                this.value = value;
            }

            public double getProgress() {
                return progress;
            }

            public T getValue() {
                return value;
            }

            @Override
            public String toString() {
                return "KeyFrame progress = " + getProgress() + "; value = " + getValue();
            }

        }

    }

    public static class BlendingTimeLine<T> {

        private Map<Float, KeyFrame<T>> mapEvents;

        private Blender<T> blender;

        public BlendingTimeLine(Blender<T> blender) {
            mapEvents = new TreeMap<>();
            this.blender = blender;
        }

        public void setBlender(Blender<T> blender) {
            this.blender = blender;
        }

        public Blender<T> getBlender() {
            return blender;
        }

        public void add(float progress, T value) {
            mapEvents.put(progress, new KeyFrame<T>(progress, value));
        }

        public T getValueAt(float progress) {
            if (progress < 0) {
                progress = 0;
            } else if (progress > 1) {
                progress = 1;
            }

            List<KeyFrame<T>> keyFrames = getKeyFramesBetween(progress);

            float max = keyFrames.get(1).progress - keyFrames.get(0).progress;
            float value = progress - keyFrames.get(0).progress;
            float weight = value / max;

            T blend = blend(keyFrames.get(0).getValue(), keyFrames.get(1).getValue(), 1f - weight);
            return blend;
        }

        public List<KeyFrame<T>> getKeyFramesBetween(float progress) {

            List<KeyFrame<T>> frames = new ArrayList<>(2);
            int startAt = 0;
            Float[] keyFrames = mapEvents.keySet().toArray(new Float[mapEvents.size()]);
            while (startAt < keyFrames.length && keyFrames[startAt] <= progress) {
                startAt++;
            }

            startAt = Math.min(startAt, keyFrames.length - 1);

            frames.add(mapEvents.get(keyFrames[startAt - 1]));
            frames.add(mapEvents.get(keyFrames[startAt]));

            return frames;

        }

        protected T blend(T start, T end, float ratio) {
            return blender.blend(start, end, ratio);
        }

        public static interface Blender<T> {

            public T blend(T start, T end, float ratio);
        }

        public class KeyFrame<T> {

            private float progress;
            private T value;

            public KeyFrame(float progress, T value) {
                this.progress = progress;
                this.value = value;
            }

            public float getProgress() {
                return progress;
            }

            public T getValue() {
                return value;
            }

            @Override
            public String toString() {
                return "KeyFrame progress = " + getProgress() + "; value = " + getValue();
            }

        }

    }

    public class DoubleBlender implements BlendingTimeLine.Blender<Double> {

        @Override
        public Double blend(Double start, Double end, float ratio) {
            double ir = (double) 1.0 - ratio;
            return (double) (start * ratio + end * ir);
        }

    }

    public enum Animator {
        INSTANCE;
        private Timer timer;
        private List<Animatable> properies;

        private Animator() {
            properies = new ArrayList<>(5);
            timer = new Timer(5, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    List<Animatable> copy = new ArrayList<>(properies);
                    Iterator<Animatable> it = copy.iterator();
                    while (it.hasNext()) {
                        Animatable ap = it.next();
                        ap.tick();
                    }
                    if (properies.isEmpty()) {
                        timer.stop();
                    }
                }
            });
        }

        public void add(Animatable ap) {
            properies.add(ap);
            timer.start();
        }

        protected void removeAll(List<Animatable> completed) {
            properies.removeAll(completed);
        }

        public void remove(Animatable ap) {
            properies.remove(ap);
            if (properies.isEmpty()) {
                timer.stop();
            }
        }

    }

    // Reprepresents a linear animation
    public interface Animatable {

        public void tick();

        public void start();

        public void stop();
    }

    public interface DurationAnimatable extends Animatable {
        public Duration getDuration();
        public Double getPlayedDuration();
    }

    public abstract class AbstractAnimatable implements Animatable {

        private AnimatableListener animatableListener;
        private AnimatableLifeCycleListener lifeCycleListener;

        public AbstractAnimatable(AnimatableListener listener) {
            this(listener, null);
        }

        public AbstractAnimatable(AnimatableListener listener, AnimatableLifeCycleListener lifeCycleListener) {
            this.animatableListener = listener;
            this.lifeCycleListener = lifeCycleListener;
        }

        public AnimatableLifeCycleListener getLifeCycleListener() {
            return lifeCycleListener;
        }

        public AnimatableListener getAnimatableListener() {
            return animatableListener;
        }

        @Override
        public void tick() {
            fireAnimationChanged();
        }

        @Override
        public void start() {
            fireAnimationStarted();
            Animator.INSTANCE.add(this);
        }

        @Override
        public void stop() {
            fireAnimationStopped();
            Animator.INSTANCE.remove(this);
        }

        protected void fireAnimationChanged() {
            if (animatableListener == null) {
                return;
            }
            animatableListener.animationChanged(this);
        }

        protected void fireAnimationStarted() {
            if (lifeCycleListener == null) {
                return;
            }
            lifeCycleListener.animationStarted(this);
        }

        protected void fireAnimationStopped() {
            if (lifeCycleListener == null) {
                return;
            }
            lifeCycleListener.animationStopped(this);
        }

    }

    public interface AnimatableListener {

        public void animationChanged(Animatable animator);
    }

    public interface AnimatableLifeCycleListener {

        public void animationCompleted(Animatable animator);

        public void animationStarted(Animatable animator);

        public void animationPaused(Animatable animator);

        public void animationStopped(Animatable animator);
    }

    public class AnimatableLifeCycleListenerAdapter implements AnimatableLifeCycleListener {

        @Override
        public void animationCompleted(Animatable animator) {
        }

        @Override
        public void animationStarted(Animatable animator) {
        }

        @Override
        public void animationPaused(Animatable animator) {
        }

        @Override
        public void animationStopped(Animatable animator) {
        }

    }

    public class DefaultDurationAnimatable extends AbstractAnimatable implements DurationAnimatable {

        private Duration duration;
        private Instant startTime;

        public DefaultDurationAnimatable(Duration duration, AnimatableListener listener, AnimatableLifeCycleListener lifeCycleListener) {
            super(listener, lifeCycleListener);
            this.duration = duration;
        }

        @Override
        public Duration getDuration() {
            return duration;
        }

        @Override
        public Double getPlayedDuration() {
            if (startTime == null) {
                return 0.0;
            }
            Duration duration = getDuration();
            Duration runningTime = Duration.between(startTime, Instant.now());
            double progress = (runningTime.toMillis() / (double) duration.toMillis());

            return Math.min(1.0, Math.max(0.0, progress));
        }

        @Override
        public void tick() {
            if (startTime == null) {
                startTime = Instant.now();
                fireAnimationStarted();
            }
            fireAnimationChanged();
            if (getPlayedDuration() >= 1.0) {
                fireAnimationCompleted();
                stop();
            }
        }

        protected void fireAnimationCompleted() {
            AnimatableLifeCycleListener lifeCycleListener = getLifeCycleListener();
            if (lifeCycleListener == null) {
                return;
            }
            lifeCycleListener.animationCompleted(this);
        }

    }

}

Yes它“看起来”很复杂,yes这“看起来”很难。但当你做过几次这样的事情时,它就会变得更简单,解决方案也更有意义。

它是解耦的。它可以重复使用。它很灵活。

在这个例子中,我主要使用了paintComponent作为主要渲染引擎。但是您可以轻松地使用与某种事件驱动框架链接在一起的各个组件。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何更改 actionPerformed() 内的 Swing Timer Delay 的相关文章

随机推荐

  • 将 Instagram 图像拉至我的应用程序 [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我正在开发适用于 iPad 的 iOS 应用程序 您可以在其中从卷中导入照片来个性化茶杯 我很想添加 Instagram 这样用户就可以添
  • Python3 + PyGobject + GTK3 和 cx_freeze 缺少 DLL

    当我使用 py python3 pygobject gtk3 应用程序制作 exe 时从 pygobject 站点来看 它缺少一些 DLL 文件 缺少什么文件 我已经手动尝试过需要哪些 DLL 因此 如果这对某人有帮助 the 必须进行编辑
  • 如何将嵌套 Java Collection 中的所有项目展平到单个列表中?

    给定一个复杂的嵌套对象集合 例如 Set
  • WPF 同一系列的多个字体文件

    我有以下字体文件 MyFont Regular tff MyFont Bold tff MyFont Italic tff 我该如何使用它们 我可以做以下事情
  • 显示精灵的另一个实例

    是否可以显示精灵的另一个实例 我想做的是反射动画精灵 到目前为止 我得到的是我的 Sprite 称为 canvas 它内部有使用 AS3 进行动画处理的内容 我想要做的是显示它翻转的副本 在它下面看起来像倒影 我尝试了以下代码 但没有运气
  • 类库(传统便携式)?

    我有一台装有 Microsoft Visual Studio Community 2017 的电脑 版本 15 2 它有一个类库 便携式 的项目模板 另一台版本为 15 3 1 的 PC 有一个类库模板 Legacy Portable PC
  • 请解释如何使用CheckBoxTableCell

    我想了解更多有关如何实际使用或子类化 如果需要 CheckBoxTableCell 的信息 在一种特定情况下 我想使用此类 其中复选框不绑定到基础数据模型属性 假设我有一个名为 选择 的列 其中包含复选框 该列或多或少充当该行的视觉标记 用
  • 亚马逊s3上传多个文件android

    如果有人仍在寻找解决方案 我最终会在代码上使用循环 但我没有找到官方 api 来执行多个文件上传 我有一个 ImageFiles 的 ArrayList 我想将其上传到 Amazon s3 他们的文档提供了以下代码 credentials
  • 通过距离和摩擦力计算速度

    我正在用 Javascript Canvas HTML5 编写一个游戏 我刚刚发现了一个与高等数学相关的大问题 该游戏是平面 2D 游戏 因此您可以从另一个角度看世界 这意味着没有重力 只有摩擦力 CODE var friction 0 9
  • 当没有导航栏时,如何在 EKEventeditViewController 中获得“完成”或“后退”按钮?

    我的 iOS 应用程序中有一个日历事件列表 单击时将在 EKEventViewController 中打开 这是我的代码 void tableView UITableView tableView didSelectRowAtIndexPat
  • 如何在表格行上使用slideDown(或show)函数?

    我正在尝试向表中添加一行并将该行滑入视图中 但是 Slidedown 函数似乎向表行添加了 display block 样式 这会弄乱布局 有什么想法可以解决这个问题吗 这是代码 get some url val1 id function
  • 在 Promise catch 中重新抛出错误

    我在教程中找到了以下代码 promise then function result some code catch function error throw error 我有点困惑 catch 调用有什么作用吗 在我看来 它没有任何效果 因
  • Windows 搜索 sql - 无法访问 System.Search.QueryFocusedSummary

    我正在尝试使用 sql 查询 Windows Search 4 0 该物业 我感兴趣的是 System Search QueryFocusedSummary 我正在尝试从 SystemIndex 读取此属性 我收到 列不存在 错误消息 我能
  • 安装nvm后无法卸载全局npm包

    我发现了与此问题相关的几个线程 但似乎没有一个专门处理我的案例 并且我无法使用我找到的建议来解决 当我跑步时npm uninstall g some package 它只是返回 up to date in 043s 全球一揽子计划仍然存在
  • cakephp 3.x 保存多个实体 - newEntities

    我在保存多条记录方面遇到了最困难的时期 我已经尝试了一百万次 但最终遇到了同样的问题 我的记录没有保存 而且我看不到任何错误 请记住 我是 cakephp 的新手 也是一名新手编码员 我是否遗漏了一些明显且关键的东西 Table this
  • Google 在 React 中使用 firebase 登录 chrome 扩展

    使用 Firebase 进行 Google 登录 并使用 React 创建的 Chrome 扩展程序 我已经使用设置了 oauthGoogleConsole并能够使用 chrome 扩展程序成功登录 chrome identity getA
  • Leaflet Draw spritesheet 图标问题 - 缺失且未对齐

    我已将传单绘制纳入我的一个项目中 我的问题是图标没有显示在工具栏中 它看起来像这样 环顾四周我发现THIS https github com Leaflet Leaflet draw issues 617发布并按照其说明进行操作 我在 Le
  • 在 apache (ubuntu 12) 下将 python 脚本作为 cgi 运行时出现问题

    披露 我搜索了很多 我认为我的问题 针对我的配置 在这里没有得到解答 例如作为 cgi apache 服务器运行 python 脚本 https stackoverflow com questions 15878010 run python
  • Unity android 项目抛出“抱歉,您的硬件不支持此应用程序”错误

    我已经调查了2天了 我读了很多东西 总结一下我读到的内容 非 NEON 设备无法与 UNITY 5 版本一起使用 您应该将安装位置设置为 自动或强制内部 您应该将写入权限设置为 仅限内部 除了上述设置之外 我还尝试了这些纹理压缩设置 不要覆
  • 如何更改 actionPerformed() 内的 Swing Timer Delay

    所以我正在构建这个音乐播放器应用程序 它可以播放拖放到 JLabel 上的音符 当我按下播放按钮时 我希望每个音符都突出显示 并带有与该音符对应的延迟值 我为此使用了 Swing Timer 但问题是 它只是以构造函数中指定的恒定延迟循环