Hello I want to apply the dark theme at the login of the user in Vaadin 14. But it does not work when i call the toggle function programmatically. I was following this example:
https://vaadin.com/learn/tutorials/toggle-dark-theme
The setting is already persisted, but how can i apply the theme setting? It works only when the trigger comes from a request thread. Here is my code:
/**
* Changes the theme from dark to light and vice versa
*/
private static void toogleTheme() {
boolean darkThemeEnabled = ConfigManager.isLightThemeEnabled();
ThemeList themeList = UI.getCurrent().getElement().getThemeList(); //
if (themeList.contains(Lumo.DARK)) { //
themeList.remove(Lumo.DARK);
themeList.add(Lumo.LIGHT);
} else {
themeList.remove(Lumo.LIGHT);
themeList.add(Lumo.DARK);
}
ConfigManager.setLightThemeEnabled(!darkThemeEnabled);
}
The method gets called from the change theme button.
You can read the value in the onAttach method. Here is a full example class which uses a cookie for storage:
public class HelloWorldView extends HorizontalLayout {
private static final String LIGHTMODE = "lightmode";
private Button toggleTheme;
public HelloWorldView() {
setMargin(true);
toggleTheme = new Button("Toggle theme between light and dark");
add(toggleTheme);
toggleTheme.addClickListener(e -> {
toggleTheme();
});
}
private void setThemeFromCookie() {
ThemeList themeList = UI.getCurrent().getElement().getThemeList();
if (isLightThemeOn()) {
if (themeList.contains(Lumo.DARK)) {
themeList.remove(Lumo.DARK);
}
themeList.add(Lumo.LIGHT);
} else {
if (themeList.contains(Lumo.LIGHT)) {
themeList.remove(Lumo.LIGHT);
}
themeList.add(Lumo.DARK);
}
}
private void toggleTheme() {
boolean saveLightTheme = true;
ThemeList themeList = UI.getCurrent().getElement().getThemeList();
if (themeList.contains(Lumo.DARK)) {
themeList.remove(Lumo.DARK);
themeList.add(Lumo.LIGHT);
} else {
themeList.remove(Lumo.LIGHT);
themeList.add(Lumo.DARK);
saveLightTheme = false;
}
setLightThemeInCookie(saveLightTheme);
}
private void setLightThemeInCookie(boolean b) {
Cookie myCookie = new Cookie(LIGHTMODE, b ? "true" : "false");
// Make cookie expire in 2 minutes
myCookie.setMaxAge(120);
myCookie.setPath(VaadinService.getCurrentRequest().getContextPath());
VaadinService.getCurrentResponse().addCookie(myCookie);
}
private String getLightModeCookieValue() {
for (Cookie c : VaadinService.getCurrentRequest().getCookies()) {
if ("lightmode".equals(c.getName())) {
String value = c.getValue();
return value;
}
}
return null;
}
private boolean isLightThemeOn() {
String value = getLightModeCookieValue();
if (value == null) {
setLightThemeInCookie(true);
return true;
}
return "true".equals(value);
}
#Override
protected void onAttach(AttachEvent attachEvent) {
setThemeFromCookie();
super.onAttach(attachEvent);
}
}
So I am relatively new to programming, and I have been working on this task app, where I want to save the data such as task name and more, given by the user. I am trying to accomplish this using Room. Now, initially, when I tried to do it, the app would crash since I was doing everything on the main thread probably. So, after a little research, I came to AsyncTask, but that is outdated. Now finally I have come across the Executer. I created a class for it, but I am a little unsure as to how I can implement it in my app. This is what I did :
Entity Class :
#Entity (tableName = "subtasks")
public class SubtaskDetails {
#PrimaryKey(autoGenerate = true)
private int id;
#ColumnInfo(name = "subtaskName")
private String subtasksName;
#ColumnInfo(name = "priority")
private boolean priHigh;
private boolean priMed;
private boolean priLow;
#ColumnInfo(name = "time")
private boolean timeMore;
private boolean timeMed;
private boolean timeLess;
public SubtaskDetails(String subtasksName, boolean priHigh, boolean priMed, boolean priLow, boolean timeMore, boolean timeMed, boolean timeLess){
this.subtasksName = subtasksName;
this.priHigh = priHigh;
this.priMed = priMed;
this.priLow = priLow;
this.timeMore = timeMore;
this.timeMed = timeMed;
this.timeLess = timeLess;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getSubtasksName() {
return subtasksName;
}
public void setSubtasksName(String subtasksName) {
this.subtasksName = subtasksName;
}
public boolean isPriHigh() {
return priHigh;
}
public void setPriHigh(boolean priHigh) {
this.priHigh = priHigh;
}
public boolean isPriMed() {
return priMed;
}
public void setPriMed(boolean priMed) {
this.priMed = priMed;
}
public boolean isPriLow() {
return priLow;
}
public void setPriLow(boolean priLow) {
this.priLow = priLow;
}
public boolean isTimeMore() {
return timeMore;
}
public void setTimeMore(boolean timeMore) {
this.timeMore = timeMore;
}
public boolean isTimeMed() {
return timeMed;
}
public void setTimeMed(boolean timeMed) {
this.timeMed = timeMed;
}
public boolean isTimeLess() {
return timeLess;
}
public void setTimeLess(boolean timeLess) {
this.timeLess = timeLess;
}
}
Dao Class:
#Dao
public interface UserDao {
#Query("Select * from subtasks")
List<SubtaskDetails> getSubtaskDetailsList();
#Insert
void insertSubtaskDetails(SubtaskDetails subtaskDetails);
#Update
void updateSubtaskDetails(SubtaskDetails subtaskDetails);
#Delete
void deleteSubtaskDetails(SubtaskDetails subtaskDetails);
}
App Database Class :
#Database(entities = SubtaskDetails.class, exportSchema = false, version = 1)
public abstract class AppDatabase extends RoomDatabase {
private static final String DB_NAME = "subtaskdetails_db";
private static AppDatabase instance;
public static synchronized AppDatabase getInstance(Context context){
if (instance == null){
instance = Room.databaseBuilder(context.getApplicationContext(), AppDatabase.class, DB_NAME).fallbackToDestructiveMigration().build();
}
return instance;
}
public abstract UserDao subtaskdetailsdao();
}
Executer Class :
public class AppExecutors {
// For Singleton instantiation
private static final Object LOCK = new Object();
private static AppExecutors sInstance;
private final Executor diskIO;
private final Executor mainThread;
private final Executor networkIO;
private AppExecutors(Executor diskIO, Executor networkIO, Executor mainThread) {
this.diskIO = diskIO;
this.networkIO = networkIO;
this.mainThread = mainThread;
}
public static AppExecutors getInstance() {
if (sInstance == null) {
synchronized (LOCK) {
sInstance = new AppExecutors(Executors.newSingleThreadExecutor(),
Executors.newFixedThreadPool(3),
new MainThreadExecutor());
}
}
return sInstance;
}
public Executor diskIO() {
return diskIO;
}
public Executor mainThread() {
return mainThread;
}
public Executor networkIO() {
return networkIO;
}
private static class MainThreadExecutor implements Executor {
private Handler mainThreadHandler = new Handler(Looper.getMainLooper());
#Override
public void execute(#NonNull Runnable command) {
mainThreadHandler.post(command);
}
}
}
Where I try to implement it : (I have written a comment where I do it)
public class TaskInfo extends AppCompatActivity {
//Declaring variables
EditText etWorkingHours, etWorkingMinutes, etTaskName, etWorkingMins, etWorkinghrs, etSubtaskName;
Button btnNewSubtask;
Button btnSaveTaskName;
Button btnProceed;
ImageView ivLeft, ivRight;
TextView tvBreakTime;
TextView tvTaskName;
int breaktime = 10;
final int ENTER_SUBTASK = 20;
final int EDIT_SUBTASK = 40;
ListView lvSubtasks;
ArrayList<subtask> subtaskList = new ArrayList<>();
ScrollView scrollView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_task_info);
//Connecting XML to JAVA
etWorkingHours = findViewById(R.id.etWorkingHours);
etWorkingMinutes = findViewById(R.id.etWorkingMinutes);
etTaskName = findViewById(R.id.etTaskName);
btnNewSubtask = findViewById(R.id.btnNewSubtask);
ivLeft = findViewById(R.id.ivLeft);
ivRight = findViewById(R.id.ivRight);
tvBreakTime = findViewById(R.id.tvBreakTime);
etWorkinghrs = findViewById(R.id.etWorkingHrs);
etWorkingMins = findViewById(R.id.etWorkingMins);
lvSubtasks = findViewById(R.id.lvSubtasks);
scrollView = findViewById(R.id.scrollView);
btnSaveTaskName = findViewById(R.id.btnSaveTask);
tvTaskName = findViewById(R.id.tvTaskName);
btnProceed = findViewById(R.id.btnProceed);
tvTaskName.setVisibility(View.INVISIBLE);
if (tvTaskName.getText().equals(""))
{
tvTaskName.setClickable(false);
}
else
{
tvTaskName.setClickable(true);
}
// Code for the left right arrows along with break duration
ivRight.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (breaktime >= 10 && breaktime < 60)
{breaktime += 5;
String time = breaktime + "";
tvBreakTime.setText(time);}
else
{
String time = breaktime + "";
tvBreakTime.setText(time);
}
}
});
ivLeft.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (breaktime > 10 && breaktime <= 60)
{
breaktime -= 5;
String time = breaktime + "";
tvBreakTime.setText(time);}
else
{
String time = breaktime + "";
tvBreakTime.setText(time);
}
}
});
btnNewSubtask.setEnabled(false);
btnSaveTaskName.setEnabled(false);
//save button enabler when task name is written
etTaskName.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
btnSaveTaskName.setEnabled(!TextUtils.isEmpty(s.toString().trim()));
}
#Override
public void afterTextChanged(Editable s) {
}
});
btnSaveTaskName.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
tvTaskName.setVisibility(View.VISIBLE);
tvTaskName.setText(etTaskName.getText().toString().toUpperCase().trim());
etTaskName.setVisibility(View.GONE);
btnSaveTaskName.setVisibility(View.GONE);
btnNewSubtask.setEnabled(true);
}
});
tvTaskName.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String tasksname = tvTaskName.getText().toString().trim();
tvTaskName.setText("");
etTaskName.setVisibility(View.VISIBLE);
etTaskName.setText(tasksname);
btnSaveTaskName.setVisibility(View.VISIBLE);
}
});
btnNewSubtask.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent i2 = new Intent(TaskInfo.this, SubtaskActivity.class);
startActivityForResult(i2, ENTER_SUBTASK);
overridePendingTransition(R.anim.slide_in_up, R.anim.slide_out_up);
}
});
btnProceed.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (etWorkingHours.getText().toString().isEmpty())
{
etWorkingHours.setText("0");
}
if (etWorkingMinutes.getText().toString().isEmpty())
{
etWorkingMinutes.setText("0");
}
if (etWorkinghrs.getText().toString().isEmpty())
{
etWorkinghrs.setText("0");
}
if (etWorkingMins.getText().toString().isEmpty())
{
etWorkingMins.setText("0");
}
int working_hours = Integer.parseInt(etWorkinghrs.getText().toString().trim());
int working_minutes = Integer.parseInt(etWorkingMins.getText().toString().trim());
int without_break_hours = Integer.parseInt(etWorkingHours.getText().toString().trim());
int without_break_minutes = Integer.parseInt(etWorkingMinutes.getText().toString().trim());
if (etWorkingHours.getText().toString().isEmpty() || etWorkingMinutes.getText().toString().isEmpty() || etWorkinghrs.getText().toString().isEmpty() || etWorkingMins.getText().toString().isEmpty())
{
Toast.makeText(TaskInfo.this, "Field cannot be empty, please try again.", Toast.LENGTH_SHORT).show();
}
else
{
if (working_hours != 0)
{
if (working_hours > without_break_hours)
{
int breaktime = Integer.parseInt(tvBreakTime.getText().toString());
Intent intent = new Intent(TaskInfo.this, TaskSummary.class);
intent.putExtra("working_hours", working_hours);
intent.putExtra("working_minutes", working_minutes);
intent.putExtra("without_break_hours", without_break_hours);
intent.putExtra("without_break_minutes", without_break_minutes);
intent.putExtra("break_duration", breaktime);
startActivity(intent);
}
if (working_hours == without_break_hours){
if (working_minutes >= without_break_minutes){
int breaktime = Integer.parseInt(tvBreakTime.getText().toString());
Intent intent = new Intent(TaskInfo.this, TaskSummary.class);
intent.putExtra("working_hours", working_hours);
intent.putExtra("working_minutes", working_minutes);
intent.putExtra("without_break_hours", without_break_hours);
intent.putExtra("without_break_minutes", without_break_minutes);
intent.putExtra("break_duration", breaktime);
startActivity(intent);
}
if (working_minutes < without_break_minutes){
Toast.makeText(TaskInfo.this, "Invalid Time Entered", Toast.LENGTH_SHORT).show();
}
}
if (working_hours < without_break_hours){
Toast.makeText(TaskInfo.this, "Invalid Time Entered", Toast.LENGTH_SHORT).show();
}
}
if (working_hours == 0){
if (without_break_hours == 0)
{
if (working_minutes >= without_break_minutes){
int breaktime = Integer.parseInt(tvBreakTime.getText().toString());
Intent intent = new Intent(TaskInfo.this, TaskSummary.class);
intent.putExtra("working_hours", working_hours);
intent.putExtra("working_minutes", working_minutes);
intent.putExtra("without_break_hours", without_break_hours);
intent.putExtra("without_break_minutes", without_break_minutes);
intent.putExtra("break_duration", breaktime);
startActivity(intent);
}
if (working_minutes < without_break_minutes){
Toast.makeText(TaskInfo.this, "Invalid Time Entered", Toast.LENGTH_SHORT).show();
}
}
if (without_break_hours != 0)
{
Toast.makeText(TaskInfo.this, "Invalid Time Entered", Toast.LENGTH_SHORT).show();
}
}
}
}
});
//Applying the max min thing for which the class InputFilterMinMax is defined
etWorkingHours.setFilters(new InputFilter[]{new InputFilterMinMax("0", "24")});
etWorkingMinutes.setFilters(new InputFilter[]{new InputFilterMinMax("0", "59")});
etWorkinghrs.setFilters(new InputFilter[]{new InputFilterMinMax("0", "24")});
etWorkingMins.setFilters(new InputFilter[]{new InputFilterMinMax("0", "59")});
}
// This is where I try to implement the executer :
#Override
protected void onActivityResult(int requestCode, int resultCode, #Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == ENTER_SUBTASK)
{
if (resultCode == RESULT_OK)
{
SubtaskAdapter adapter = new SubtaskAdapter(this, subtaskList);
assert data != null;
String subtaskName = data.getStringExtra("subtaskName");
boolean priHigh = data.getBooleanExtra("priHigh", false);
boolean priMed = data.getBooleanExtra("priMed", false);
boolean priLow = data.getBooleanExtra("priLow", false);
boolean timeMore = data.getBooleanExtra("timeMore", false);
boolean timeMed = data.getBooleanExtra("timeMed", false);
boolean timeLess = data.getBooleanExtra("timeLess", false);
AppDatabase appDb = AppDatabase.getInstance(this);
AppExecutors.getInstance().diskIO().execute(new Runnable() {
#Override
public void run() {
SubtaskDetails subtaskDetails = new SubtaskDetails(subtaskName,priHigh, priMed, priLow, timeMore, timeMed, timeLess);
appDb.subtaskdetailsdao().insertSubtaskDetails(subtaskDetails);
}
});
lvSubtasks.setAdapter(adapter);
subtask subtask = new subtask(subtaskName, priHigh, priMed, priLow, timeMore, timeMed, timeLess);
subtaskList.add(subtask);
adapter.addANewSubTask(subtask);
}
}
}
// Menu (action bar) code
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return super.onCreateOptionsMenu(menu);
}
#Override
public boolean onOptionsItemSelected(#NonNull MenuItem item) {
switch (item.getItemId())
{
case (R.id.settings):
startActivity(new Intent(this, SettingsActivity.class));
break;
}
return super.onOptionsItemSelected(item);
}
//Class to make max and min value on the edit text of hours and minutes, so that they cant enter anything more than 24 hours, and 59 minutes
static class InputFilterMinMax implements InputFilter {
private int min, max;
public InputFilterMinMax(int min, int max) {
this.min = min;
this.max = max;
}
public InputFilterMinMax(String min, String max) {
this.min = Integer.parseInt(min);
this.max = Integer.parseInt(max);
}
#Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
try {
int input = Integer.parseInt(dest.toString() + source.toString());
if (isInRange(min, max, input))
return null;
} catch (NumberFormatException nfe) { }
return "";
}
private boolean isInRange(int a, int b, int c) {
return b > a ? c >= a && c <= b : c >= b && c <= a;
}
}
}
EDIT :
I implemented the code, but I am facing a few issues in the viewmodel class. For eg, the parameter in repository.insertSubtaskDetails(userDao); is userDao, but it actually it requires of type subtaskDetails.. some errors in the todo part as well. this is the code:
package com.example.taskmasterv3;
import android.app.Application;
import androidx.annotation.NonNull;
import androidx.lifecycle.AndroidViewModel;
import java.util.List;
public class ViewModel extends AndroidViewModel {
private Repository repository;
private List<SubtaskDetails> subtaskDetails;
public ViewModel(#NonNull Application application) {
super(application);
repository = new Repository(application);
subtaskDetails = repository.getSubtaskDetails();
}
public void insert(UserDao userDao) {
repository.insertSubtaskDetails(userDao);
}
public void delete(Todo todo) {
repository.deleteSubtaskDetails(userDao);
}
}
First make a Repository class and make an instance of your DAO
public class Repository {
private UserDAO userDAO;
private List<SubtaskDetails> subtaskDetails;
private Executor executor = Executors.newSingleThreadExecutor();
public Repository(Application application){
AppDatabase appDatabase = AppDatabase.getInstance(application);
userDAO = appDatabase.userDAO();
subtaskDetails = userDAO.getSubtaskDetailsList();
}
Then wrap it around your executor
public void insertSubtaskDetails(SubtaskDetails subtaskDetails){
executor.execute(new Runnable() {
#Override
public void run() {
userDAO.insertSubtaskDetails(subtaskDetails);
}
});
}// For inserting
public void deleteSubtaskDetails(SubtaskDetails subtaskDetails){
executor.execute(new Runnable() {
#Override
public void run() {
userDAO.deleteSubtaskDetails(subtaskDetails);
}
});
}// for deleting
Basically just wrap your DAO queries around the executor.
I'm trying to wrote android program which displays Greatest Common Divisor of two integers specified in two different EditText fields. First I've done it with button, everything worked (you can see onclick listener commented out in code below). Now I want to do this: app checks when both EditTexts are not empty and then automatically starts calculating and shows gcd. Buty app crashes when I start typing in any of EditText fields. Also I tried to add TextChangeListener only on one of EditTexts. Everything is good until I delete all input from one of the fields, then app crashes again. I'm only starting to understand android development and made this app mostly by modifying examples found on internet so maybe I did something wrong... Can anyone help me? Thanks
MainActivity.java
public class MainActivity extends Activity
{
EditText a;
EditText b;
TextView gcdResult;
Button calculateGcd;
int a, b, gcdValue
TextWatcher textWatcher = new TextWatcher(){
#Override
public void afterTextChanged(Editable s){}
#Override
public void beforeTextChanged(CharSequence s,int start, int count, int after){}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count){
AutoCalculateGcd();
}
};
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
a = (EditText)findViewById(R.id.aText1);
b = (EditText)findViewById(R.id.bText1);
gcdResult = (TextView)findViewById(R.id.resultTextView1);
calculateGcd = (Button)findViewById(R.id.calcButton1);
/* calculateGcd.setOnClickListener(new OnClickListener(){
public void onClick(View v){
AutoCalculateRatio();
}
});*/
a.addTextChangedListener(textWatcher);
b.addTextChangedListener(textWatcher);
}
//Euclidean alghorithm to find gcd
public static int gcd(int a, int b) {
if (b == 0) return w;
else return gcd(b a % b);
}
public static boolean isInputNotEmpty(EditText a, EditText b){
String a = a.getText().toString();
String b = b.getText().toString();
if(a.equals("") && b.equals("") ){
return false;
}
else{
return true;
}
}
public void AutoCalculateGcd(){
if(isInputNotEmpty(a, b)){
a = Integer.parseInt(width.getText().toString());
b = Integer.parseInt(height.getText().toString());
gcdValue = gcd(a, b);
ratioResult.setText(Integer.toString(gcdValue));
}
else{
//Toast.makeText(this, "No input", Toast.LENGTH_SHORT).show();
}
}
}
Actually, you should replace
public static boolean isInputNotEmpty(EditText a, EditText b) {
String a = a.getText().toString();
String b = b.getText().toString();
if (a.equals("") && b.equals("")) {
return false;
}
else {
return true;
}
}
with
public static boolean isInputNotEmpty(EditText a, EditText b) {
String a = a.getText().toString();
String b = b.getText().toString();
if (a.equals("") || b.equals("")) {
return false;
}
else {
return true;
}
}
Or even
public static boolean isInputNotEmpty(EditText a, EditText b) {
return !(a.getText().toString().isEmpty() || b.getText().toString().isEmpty());
}
Because you want to know if any ( || ) of them is empty, not if both (&&) are.
It might help if you post the stacktrace, but my guess is that you are getting a NumberFormatException from the Integer.parseInt() calls. One approach would be to do something like:
try {
a = Integer.parseInt(width.getText().toString());
b = Integer.parseInt(height.getText().toString());
gcdValue = gcd(a, b);
ratioResult.setText(Integer.toString(gcdValue));
} catch ( NumberFormatException e) {
ratioResult.setText("N/A")
}
I am trying to implement a ImageView which will have different reaction with each action(Drag, DoubleTap and Single Tap). What I am doing is based in this tutorial:
DoubleTap in android
Which I used to create my class:
public class MyIcon extends ImageView {
GestureDetector gestureDetector;
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
// creating new gesture detector
gestureDetector = new GestureDetector(context, new GestureListener());
}
// skipping measure calculation and drawing
// delegate the event to the gesture detector
#Override
public boolean onTouchEvent(MotionEvent e) {
return gestureDetector.onTouchEvent(e);
}
private class GestureListener extends GestureDetector.SimpleOnGestureListener {
#Override
public boolean onDown(MotionEvent e) {
return true;
}
// event when double tap occurs
#Override
public boolean onDoubleTap(MotionEvent e) {
//A Toast just to see if it is working
return true;
}
#Override
public boolean onShowPress(MotionEvent e) {
//A Toast just to see if it is working
return true;
}
}
}
To instantiate:
private WindowManager windowManager;
private MyIcon myIcon;
private WindowManager.LayoutParams params;
windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
chatHead = new MyIcon(this);
chatHead.setImageResource(R.mipmap.ic_launcher);
params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_PHONE,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSPARENT
);
params.gravity = Gravity.TOP | Gravity.LEFT;
params.x = 0;
params.y = 100;
I used a setOnTouchListener() which worked fine to make do Drag, but it is not work to DoubleTap.
Which would be the correct way to implement and instantiate this class to both work?
try this may help you SimpleGestureFilter
public class SimpleGestureFilter extends SimpleOnGestureListener {
public final static int SWIPE_UP = 1;
public final static int SWIPE_DOWN = 2;
public final static int SWIPE_LEFT = 3;
public final static int SWIPE_RIGHT = 4;
public final static int MODE_TRANSPARENT = 0;
public final static int MODE_SOLID = 1;
public final static int MODE_DYNAMIC = 2;
private final static int ACTION_FAKE = -13; // just an unlikely number
private int swipe_Min_Distance = 100;
private int swipe_Max_Distance = 350;
private int swipe_Min_Velocity = 100;
private int mode = MODE_DYNAMIC;
private boolean running = true;
private boolean tapIndicator = false;
private Activity context;
private GestureDetector detector;
private SimpleGestureListener listener;
public SimpleGestureFilter(Activity context, SimpleGestureListener sgl) {
this.context = context;
this.detector = new GestureDetector(context, this);
this.listener = sgl;
}
public void onTouchEvent(MotionEvent event) {
if (!this.running)
return;
boolean result = this.detector.onTouchEvent(event);
if (this.mode == MODE_SOLID)
event.setAction(MotionEvent.ACTION_CANCEL);
else if (this.mode == MODE_DYNAMIC) {
if (event.getAction() == ACTION_FAKE)
event.setAction(MotionEvent.ACTION_UP);
else if (result)
event.setAction(MotionEvent.ACTION_CANCEL);
else if (this.tapIndicator) {
event.setAction(MotionEvent.ACTION_DOWN);
this.tapIndicator = false;
}
}
// else just do nothing, it's Transparent
}
public void setMode(int m) {
this.mode = m;
}
public int getMode() {
return this.mode;
}
public void setEnabled(boolean status) {
this.running = status;
}
public void setSwipeMaxDistance(int distance) {
this.swipe_Max_Distance = distance;
}
public void setSwipeMinDistance(int distance) {
this.swipe_Min_Distance = distance;
}
public void setSwipeMinVelocity(int distance) {
this.swipe_Min_Velocity = distance;
}
public int getSwipeMaxDistance() {
return this.swipe_Max_Distance;
}
public int getSwipeMinDistance() {
return this.swipe_Min_Distance;
}
public int getSwipeMinVelocity() {
return this.swipe_Min_Velocity;
}
#Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
final float xDistance = Math.abs(e1.getX() - e2.getX());
final float yDistance = Math.abs(e1.getY() - e2.getY());
if (xDistance > this.swipe_Max_Distance
|| yDistance > this.swipe_Max_Distance)
return false;
velocityX = Math.abs(velocityX);
velocityY = Math.abs(velocityY);
boolean result = false;
if (velocityX > this.swipe_Min_Velocity
&& xDistance > this.swipe_Min_Distance) {
if (e1.getX() > e2.getX()) // right to left
this.listener.onSwipe(SWIPE_LEFT);
else
this.listener.onSwipe(SWIPE_RIGHT);
result = true;
} else if (velocityY > this.swipe_Min_Velocity
&& yDistance > this.swipe_Min_Distance) {
if (e1.getY() > e2.getY()) // bottom to up
this.listener.onSwipe(SWIPE_UP);
else
this.listener.onSwipe(SWIPE_DOWN);
result = true;
}
return result;
}
#Override
public boolean onSingleTapUp(MotionEvent e) {
this.tapIndicator = true;
return false;
}
#Override
public boolean onDoubleTap(MotionEvent arg) {
this.listener.onDoubleTap();
;
return true;
}
#Override
public boolean onDoubleTapEvent(MotionEvent arg) {
return true;
}
#Override
public boolean onSingleTapConfirmed(MotionEvent arg) {
if (this.mode == MODE_DYNAMIC) { // we owe an ACTION_UP, so we fake an
arg.setAction(ACTION_FAKE); // action which will be converted to an
// ACTION_UP later.
this.context.dispatchTouchEvent(arg);
}
return false;
}
static interface SimpleGestureListener {
void onSwipe(int direction);
void onDoubleTap();
}
}
Use in code this way
private SimpleGestureFilter detector;
detector = new SimpleGestureFilter(this, this);
Just change in this method
#Override
public boolean dispatchTouchEvent(MotionEvent me) {
// Call onTouchEvent of SimpleGestureFilter class
this.detector.onTouchEvent(me);
return super.dispatchTouchEvent(me);
}
I'm trying to create an implementation of TransformList that maintains a list of distinct values off a source list. However, I'm a little puzzled on how the implementation should add the distinct values to my hashmap and distinct list that are contained internally. I think my ListChangeListener.change should work though. But how do I intercept any new or removed distinct values and add/remove them to the distinct map and list?
public class DistinctList<E> extends TransformationList<E,E> {
private final ObservableList<E> distinctList = FXCollections.observableArrayList();
private final ConcurrentHashMap<E,E> distinctValues = new ConcurrentHashMap<>();
private final ObservableList<E> source;
public DistinctList(ObservableList<E> source) {
super(source);
this.source = source;
source.stream().filter(s -> attemptAdd(s)).forEach(s -> distinctList.add(s));
}
private boolean attemptAdd(E e) {
final boolean result = distinctValues.putIfAbsent(e,e) == null;
if (result) {
distinctList.add(e);
}
return result;
}
private boolean attemptRemove(E e) {
final boolean result = distinctValues.remove(e, e);
if (result) {
distinctList.remove(e);
}
return result;
}
#Override
protected void sourceChanged(ListChangeListener.Change<? extends E> c) {
fireChange(new ListChangeListener.Change<E>(this) {
#Override
public boolean wasAdded() {
if (c.getAddedSubList().stream().filter(v -> distinctValues.contains(v) == false).findAny().isPresent()) {
return true;
}
else {
return false;
}
}
#Override
public boolean wasRemoved() {
if (c.getRemoved().stream().filter(v -> !source.contains(v)).findAny().isPresent()) {
return true;
}
else {
return false;
}
}
#Override
public boolean wasPermutated() {
return false;
}
#Override
protected int[] getPermutation() {
throw new AssertionError("getPermutation() not implemented");
}
#Override
public List<E> getRemoved() {
return c.getRemoved().stream().filter(v -> !source.contains(v)).collect(Collectors.toList());
}
#Override
public int getFrom() {
return 0;
}
#Override
public int getTo() {
return 0;
}
#Override
public boolean next() {
return c.next();
}
#Override
public void reset() {
c.reset();
}
});
}
#Override
public int getSourceIndex(int index) {
return IntStream.range(0,source.size()).filter(i -> source.get(i).equals(this.get(i))).findAny().orElse(-1);
}
#Override
public E get(int index) {
return distinctList.get(index);
}
#Override
public int size() {
return distinctList.size();
}
}
UPDATE
I kept working with this and I think I figured out where to interact source changes with the distinct value map and list. But when my source list removes a value (and other values with same hashcode/equals still exists), it wrongly removes the value from the distinct values. What am I doing wrong?
public class DistinctList<E> extends TransformationList<E,E> {
private final ObservableList<E> distinctList = FXCollections.observableArrayList();
private final ConcurrentHashMap<E,E> distinctValues = new ConcurrentHashMap<>();
private final ObservableList<E> source;
public DistinctList(ObservableList<E> source) {
super(source);
this.source = source;
source.stream().forEach(s -> attemptAdd(s));
}
private boolean attemptAdd(E e) {
final boolean result = distinctValues.putIfAbsent(e,e) == null;
if (result) {
distinctList.add(e);
}
return result;
}
private boolean attemptRemove(E e) {
final boolean result = distinctValues.remove(e, e);
if (result) {
distinctList.remove(e);
}
return result;
}
#Override
protected void sourceChanged(ListChangeListener.Change<? extends E> c) {
ListChangeListener.Change<E> change = new ListChangeListener.Change<E>(this) {
#Override
public boolean wasAdded() {
if (c.getAddedSubList().stream().filter(v -> source.contains(v)).findAny().isPresent()) {
return true;
}
else {
return false;
}
}
#Override
public boolean wasRemoved() {
if (c.getRemoved().stream().filter(v -> source.contains(v) == false).findAny().isPresent()) {
return true;
}
else {
return false;
}
}
#Override
public boolean wasPermutated() {
return false;
}
#Override
protected int[] getPermutation() {
throw new AssertionError("getPermutation() not implemented");
}
#Override
public List<E> getRemoved() {
return c.getRemoved().stream().filter(v -> source.contains(v) == false)
.collect(Collectors.toList());
}
#Override
public int getFrom() {
return 0;
}
#Override
public int getTo() {
return 0;
}
#Override
public boolean next() {
return c.next();
}
#Override
public void reset() {
c.reset();
}
};
while (c.next()) {
if (c.wasAdded()) {
c.getAddedSubList().stream().filter(v -> !distinctValues.containsKey(v)).peek(a -> System.out.println("ADDING FROM MAP " + a)).forEach(a -> attemptAdd(a));
}
if (c.wasRemoved()) {
c.getRemoved().stream().filter(v -> distinctValues.containsKey(v)).peek(a -> System.out.println("REMOVING FROM MAP " + a)).forEach(a -> attemptRemove(a));
}
}
fireChange(change);
}
#Override
public int getSourceIndex(int index) {
return IntStream.range(0,source.size()).filter(i -> source.get(i).equals(this.get(i))).findAny().orElse(-1);
}
#Override
public E get(int index) {
return distinctList.get(index);
}
#Override
public int size() {
return distinctList.size();
}
}
I think I got it. Let me know if I'm missing anything.
public class DistinctList<E> extends TransformationList<E,E> {
private final ObservableList<E> distinctList = FXCollections.observableArrayList();
private final ConcurrentHashMap<E,E> distinctValues = new ConcurrentHashMap<>();
private final ObservableList<E> source;
public DistinctList(ObservableList<E> source) {
super(source);
this.source = source;
source.stream().forEach(s -> attemptAdd(s));
}
private boolean attemptAdd(E e) {
final boolean result = distinctValues.putIfAbsent(e,e) == null;
if (result) {
distinctList.add(e);
}
return result;
}
private boolean attemptRemove(E e) {
final boolean result = distinctValues.remove(e, e);
if (result) {
distinctList.remove(e);
}
return result;
}
#Override
protected void sourceChanged(ListChangeListener.Change<? extends E> c) {
while (c.next()) {
ListChangeListener.Change<E> change = new ListChangeListener.Change<E>(this) {
#Override
public boolean wasAdded() {
if (c.getAddedSubList().stream().filter(v -> distinctValues.contains(v) == false).findAny().isPresent()) {
return true;
} else {
return false;
}
}
#Override
public List<E> getAddedSubList() {
return c.getAddedSubList().stream().filter(v -> distinctValues.contains(v) == false).collect(Collectors.toList());
}
#Override
public boolean wasRemoved() {
if (c.getRemoved().stream().filter(v -> source.contains(v) == false).findAny().isPresent()) {
return true;
} else {
return false;
}
}
#Override
public boolean wasPermutated() {
return false;
}
#Override
protected int[] getPermutation() {
throw new AssertionError("getPermutation() not implemented");
}
#Override
public List<E> getRemoved() {
return c.getRemoved().stream().filter(v -> source.contains(v) == false)
.collect(Collectors.toList());
}
#Override
public int getFrom() {
return 0;
}
#Override
public int getTo() {
return 0;
}
#Override
public boolean next() {
return c.next();
}
#Override
public void reset() {
c.reset();
}
};
if (change.wasAdded()) {
change.getAddedSubList().stream().filter(v -> !distinctValues.containsKey(v)).peek(a -> System.out.println("ADDING FROM MAP " + a)).forEach(a -> attemptAdd(a));
}
if (change.wasRemoved()) {
change.getRemoved().stream().filter(v -> distinctValues.containsKey(v)).peek(a -> System.out.println("REMOVING FROM MAP " + a)).forEach(a -> attemptRemove(a));
}
fireChange(change);
}
}
#Override
public int getSourceIndex(int index) {
return IntStream.range(0,source.size()).filter(i -> source.get(i).equals(this.get(i))).findAny().orElse(-1);
}
#Override
public E get(int index) {
return distinctList.get(index);
}
#Override
public int size() {
return distinctList.size();
}
}