Asked  7 Months ago    Answers:  5   Viewed   71 times

In passive rendering mode one can use KeyListener and ActionListener interfaces to handle events from user.

What is the correct way of event handling in full screen mode? Please extend this skeleton providing implementation for mouse click and key press events, please don't bloat your example (the example starts full screen exclusive mode, using a Timer to update graphics in window):

import java.applet.Applet;
import java.awt.Color;
import java.awt.DisplayMode;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferStrategy;
import javax.swing.JFrame;
import javax.swing.Timer;

public class applet extends Applet
{
    Timer timer;
    JFrame frame;
    DisplayMode[] displayModes = new DisplayMode[] {
            new DisplayMode(1280, 800, 32, 60)
    };

    BufferStrategy bufferStrategy;
    Rectangle bounds;

    /**
     * 
     */
    private static final long serialVersionUID = 1L;

    /**
     * @param args
     */

    public void init()
    {

        GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment(); //displays, fonts, color shemes...
        GraphicsDevice device = env.getDefaultScreenDevice(); //for one-display systems

        setIgnoreRepaint(true);

        GraphicsConfiguration gc = device.getDefaultConfiguration();
        frame = new JFrame(gc);

        device.setFullScreenWindow(frame);

        if (device.isDisplayChangeSupported())
            device.setDisplayMode(displayModes[0]);

        frame.createBufferStrategy(2);
        bufferStrategy = frame.getBufferStrategy();

        timer = new Timer(1000 / 50, new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent arg0) {
                Graphics2D g = null;
                try {
                    g = (Graphics2D) bufferStrategy.getDrawGraphics();
                    render(g);
                } finally {
                    g.dispose();
                }
                bufferStrategy.show();
            }

        });

    }

    private void render(Graphics2D g) {
        g.setColor(Color.BLACK);
        g.fillRect(0, 0, bounds.width, bounds.height);
    }

    public void start()
    {
        timer.start();

    }

    public void stop()
    {
        timer.stop();
    }

}

 Answers

94

It looks like the usual approaches shown in How to Use Key Bindings and How to Write a Mouse Listener work correctly in Full-Screen Exclusive Mode.

import java.awt.Color;
import java.awt.EventQueue;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowEvent;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;

/** @see http://stackoverflow.com/questions/7456227 */
public class FullScreenTest extends JPanel {

    private static final String EXIT = "Exit";
    private JFrame f = new JFrame("FullScreenTest");
    private Action exit = new AbstractAction(EXIT) {

            @Override
            public void actionPerformed(ActionEvent e) {
                f.dispatchEvent(new WindowEvent(
                    f, WindowEvent.WINDOW_CLOSING));
            }
        };
    private JButton b = new JButton(exit);

    public FullScreenTest() {
        this.add(b);
        f.getRootPane().setDefaultButton(b);
        this.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_Q, 0), EXIT);
        this.getActionMap().put(EXIT, exit);
        this.addMouseMotionListener(new MouseAdapter() {

            @Override
            public void mouseMoved(MouseEvent e) {
                FullScreenTest.this.setToolTipText(
                    "("+ e.getX() + "," + e.getY() + ")");
            }
        });
    }

    private void display() {
        GraphicsEnvironment env =
            GraphicsEnvironment.getLocalGraphicsEnvironment();
        GraphicsDevice dev = env.getDefaultScreenDevice();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setBackground(Color.darkGray);
        f.setResizable(false);
        f.setUndecorated(true);
        f.add(this);
        f.pack();
        dev.setFullScreenWindow(f);
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                new FullScreenTest().display();
            }
        });
    }
}
Tuesday, June 1, 2021
 
Palladium
answered 7 Months ago
92

the window manager usually doesn't enforce application window modality and for custom display mode application the wm is forced to either to drop you out of fullscreen or minimize as it cannot respect the dpi setting for the other window maintaining your own window resolution.

thee is a reason for that, explained here in the context of the Window O.S.

is this for some sort of kiosk system? then make your whole application the shell, as explained here: Keeping a Windows application on top of other windows and in focus - always

task manager will still pop in front of it, and alt tabbing should work as usual.

similar step for making a single app login in linux: http://www.instructables.com/id/Setting-Up-Ubuntu-as-a-Kiosk-Web-Appliance/?ALLSTEPS

to disable usb fixing, see the answer to this question: https://superuser.com/questions/33986/is-it-possible-to-disable-the-scan-and-fix-message-when-inserting-an-sd-card

note that the first answer entail fixing the disk, if you scroll below there are the step to disable that specific dialog.

Friday, August 13, 2021
 
smiggle
answered 4 Months ago
26

I think here is the problem:

button[0].getInputMap().put(...);

From JComponent.getInputMap() javadoc:

Returns the InputMap that is used when the component has focus. This is convenience method for getInputMap(WHEN_FOCUSED).

So the button must have the focus to properly work. Since you're doing a calculator I'd suggest you use this instead:

button[0].getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(...);

JComponent.getInputMap(int condition):

Returns the InputMap that is used during condition.

Parameters:

condition - one of WHEN_IN_FOCUSED_WINDOW, WHEN_FOCUSED, WHEN_ANCESTOR_OF_FOCUSED_COMPONENT

Wednesday, August 25, 2021
 
buymypies
answered 4 Months ago
31

Create a CoinRepository and implement your logic there. If DB is empty or it is required to refresh data, let your repo query data from API and insert it in DB, and then fetch it from DB whenever needed.

Check this codelab to practically understand Room.

Create Coin entity, it's a good practice to keep entity/model classes name singular, and their respective table names plural.

Coin.java

@Entity(tableName = "coins")
public class Coin {
    @PrimaryKey(autoGenerate = true)
    public int id;

    @ColumnInfo(name = "name")
    public String name;

    @ColumnInfo(name = "symbol")
    public String symbol;

    @ColumnInfo(name = "slug")
    public String slug;

    @ColumnInfo(name = "circulating_supply")
    public Double circulatingSupply;

    @ColumnInfo(name = "total_supply")
    public Double totalSupply;

    @ColumnInfo(name = "max_supply")
    public Double maxSupply;

    @ColumnInfo(name = "date_added")
    public String dateAdded;

    @ColumnInfo(name = "num_market_pairs")
    public Integer numMarketPairs;

    @ColumnInfo(name = "cmc_rank")
    public Integer cmcRank;

    @ColumnInfo(name = "coin_symbol")
    public String lastName;

    @ColumnInfo(name = "last_updated")
    public String lastUpdated;

    @ColumnInfo(name = "price")
    public Double price;

    @ColumnInfo(name = "volume_24h")
    public Double volume24h;

    @ColumnInfo(name = "percent_change_1h")
    public Double percentChange1h;

    @ColumnInfo(name = "percent_change_24h")
    public Double percentChange24h;

    @ColumnInfo(name = "percent_change_7d")
    public Double percentChange7d;

    @ColumnInfo(name = "market_cap")
    public Double marketCap;
}

Create CoinDao interface and add your query methods in it, I've added additional methods like get(id) and update(coin), you can use them if needed.

CoinDao.java

@Dao
public interface CoinDao {
    @Query("SELECT * FROM coins")
    LiveData<List<Coin>> getAll();

    @Query("SELECT * FROM coins WHERE id = :id")
    LiveData<Coin> get(int id);

    @Insert
    void insertAll(List<Coin> coins);

    @Update
    public void update(Coin coin);

    @Query("DELETE FROM coins")
    void deleteAll();
}

Create your database class as follows:

DB.java

@Database(entities = {Coin.class}, version = 1)
public abstract class DB extends RoomDatabase {
    private static final String TAG = "DB";

    // DB INFO
    private static final String databaseName = "coin_database";

    // DAOs
    public abstract CoinDao coinDao();


    // INSTANCE
    private static volatile DB INSTANCE;
    private static final int NUMBER_OF_THREADS = 4;
    public static final ExecutorService databaseWriteExecutor =
            Executors.newFixedThreadPool(NUMBER_OF_THREADS);

    public static DB getInstance(final Context context) {
        if (INSTANCE == null) {
            synchronized (DB.class) {
                if (INSTANCE == null) {
                    INSTANCE = Room.databaseBuilder(context.getApplicationContext(), DB.class, databaseName)
                            .build();
                    Log.d(TAG, "New instance created...");
                }
            }
        }
        return INSTANCE;
    }
}

Create CoinRepository and add your WebAPI/DB logic in it. Notice, I've moved your API call in getCoins() method, you need to modify it according to your logic. Make sure your call response returns List<Coin>, or you'll have to create a List<Coin> from CryptoList yourself.

CoinRepository.java

public class CoinRepository {
    private static final String TAG = "Repo/Coin";

    private final CoinDao coinDao;
    private final LiveData<List<Coin>> coins;

    public CoinRepository(Application application) {
        coinDao = DB.getInstance(application).coinDao();
        coins = coinDao.getAll();

        Log.d(TAG, "New instance created...");
    }
    
    /*
        I've added boolean flag to check if data reload from Web API is compulsory.
        You can remove this flag and modify it as per your logic.
        
        DataReadyListener is a callback listener.
        Its onDataReady() method gets fired when data is ready.         
    */
    public void getCoins(boolean reload, DataReadyListener listener) {
        if(reload){
            //Modify this portion as per your logic
            ApiInterface apiInterfaceCoin5 =
                    APIClientCoin.getClient().create(ApiInterface.class);
            Map<String, String> params = new HashMap<>();
            params.put("limit", posts+"");

            apiInterfaceCoin5.doGetUserListAll(params)
                    .enqueue(new Callback<List<Coin>>() {
                        @Override
                        public void onResponse(Call<List<Coin>> call, Response<List<Coin>> response) {
                            Future<?> future = DB.databaseWriteExecutor.submit(() -> {
                                coinDao.deleteAll(); //remove this if you want to keep previous data
                                coinDao.insertAll(response.body());

                                Log.d(TAG, "Data inserted in "coins" table");
                            });

                            try {
                                future.get();
                                if (future.isDone())
                                    listener.onDataReady(coins);
                            } catch (ExecutionException e) {
                                e.printStackTrace();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }

                        @Override
                        public void onFailure(Call<List<Coin>> call, Throwable t) {
                            //Handle failure here
                        }
                    });
        }
        else{
            listener.onDataReady(coins);
        }
    }

    public LiveData<Coin> getCoin(int id) {
        return coinDao.get(id);
    }

    public interface DataReadyListener {
        void onDataReady(LiveData<List<Coin>> coins);
    }
}

And lastley create CoinViewModel. You can completely skip this step and query data directly from CoinRepository. But ViewModel has its own pros, check docs for details.

CoinViewModel.java

public class CoinViewModel extends AndroidViewModel {
    private CoinRepository repository;

    public CoinViewModel(@NonNull Application application) {
        super(application);
        repository = new CoinRepository(application);
    }

    public void getCoins(boolean reload, CoinRepository.DataReadyListener listener) {
        repository.getCoins(reload, listener);
    }

    public LiveData<Coin> getCoin(int id) {
        return repository.getCoin(id);
    }
}

Now use CoinViewModel in your Activity/Fragment, first initialize it:

UI (Activity/Fragment)

private CoinViewModel coinViewModel;

@Override
protected void onCreate(Bundle savedInstanceState) {
    ....
    coinViewModel = new ViewModelProvider(this).get(CoinViewModel.class);
    ....
}

And finally use your ViewModel to query data. If you want to reload the data from Web API send first parameter as true, if you want to reuse existing data from DB keep flag as false:

coinViewModel.getCoins(true, new CoinRepository.DataReadyListener() {
        @Override
        public void onDataReady(LiveData<List<Coin>> coins) {
            //Add or set data in adapter
            //for setData() you need to create setData() method in adapter
            //which first clears existing data and then adds new data to list and notifies about dataset change.
            coins.observe(this, coinsList -> coinsAdapter.setData(coinsList));
        }
    });

Hopefully, it helps.

Update 07/08/2021

After discussion with OP, I've updated the CoinRepository code. Now, instead of relying on boolean flag, it automatically updates data after 1 minute interval using RxAndroid:

public class CoinRepository {
    private static final String TAG = "Repo/Coin";

    private final CoinDao coinDao;
    private final LiveData<List<Coin>> coins;

    public CoinRepository(Application application) {
        coinDao = DB.getInstance(application).coinDao();
        coins = coinDao.getAll();

        loadCoinsPeriodically();

        Log.d(TAG, "New instance created...");
    }

    public LiveData<List<Coin>> getCoins() {
        return coins;
    }

    private void loadCoinsPeriodically() {
        Observable.interval(0, 1, TimeUnit.MINUTES)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(
                        aLong ->
                                RetrofitService.getClient().getLatest(500)
                                        .subscribeOn(AndroidSchedulers.mainThread())
                                        .subscribe(
                                                cryptoList ->
                                                        DB.databaseWriteExecutor.submit(() -> {
                                                            coinDao.deleteAll();
                                                            coinDao.insertAll(cryptoList.getCoins());

                                                            Log.d(TAG, "Data inserted in "coins" table");
                                                        }),
                                                throwable ->
                                                        Log.d(TAG, "API observable error: " + throwable.getMessage())),
                        throwable ->
                                Log.d(TAG, "Periodic observable error: " + throwable.getMessage()));
    }

    public LiveData<Coin> getCoin(int id) {
        return coinDao.get(id);
    }
}

I've written an example app for OP's use case, check this Github repo for complete code.

Monday, October 25, 2021
 
Connor Johnson
answered 2 Months ago
100

Well. I figure out how to do it.

Although I spend more than 5 hours to understand all things behind, but the solution is very simple.

Just overwrite 'public boolean contains(int x, int y)' method of glass panel.

public static void main(String[] args)
{
    final JFrame frame = new JFrame();
    frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
    frame.setLocationRelativeTo(null);
    frame.setSize(800, 600);

    final JSplitPane panel = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, new JPanel(), new JPanel());

    frame.getContentPane().add(panel, BorderLayout.CENTER);

    final JPanel glassPane = new JPanel(){
        @Override
        public boolean contains(int x, int y)
        {
            Component[] components = getComponents();
            for(int i = 0; i < components.length; i++)
            {
                Component component = components[i];
                Point containerPoint = SwingUtilities.convertPoint(
                    this,
                    x, y,
                    component);
                if(component.contains(containerPoint))
                {
                    return true;
                }
            }
            return false;
        }
    };
    glassPane.setOpaque(false);
    JButton button = new JButton("haha");
    button.addActionListener(new ActionListener()
    {
        public void actionPerformed(ActionEvent e)
        {
            System.out.println("haha");
        }
    });
    glassPane.add(button);
    glassPane.setBorder(BorderFactory.createLineBorder(Color.red));
    frame.setGlassPane(glassPane);

    //try to comment out this line to see the difference.
    glassPane.setVisible(true);

    frame.setVisible(true);
}
Tuesday, November 9, 2021
 
Jubair
answered 4 Weeks ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :  
Share