AndroidX itemTouchListener - androidx

I have just migrated a project to androidX and have managed to fix all teh issues except 1. which is the implimentation of the touch listener below.
private fun setItemTouchListner() {
val touchListner = object: ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT) {
override fun onMove(recyclerView: androidx.recyclerview.widget.RecyclerView?, viewHolder: androidx.recyclerview.widget.RecyclerView.ViewHolder?, target: androidx.recyclerview.widget.RecyclerView.ViewHolder?): Boolean {
return false
}
override fun onSwiped(viewHolder: androidx.recyclerview.widget.RecyclerView.ViewHolder?, direction: Int) {
var rec: ChargeRecord? = null
if (viewHolder != null) {
rec = mAdapter?.getRecord((viewHolder.adapterPosition))
}
// delete record from cloud
if (rec != null) {
firebase?.child(rec.id)?.removeValue()
recordsViewModel?.deleteRecord(rec)
}
// remove record from list
if (viewHolder != null) {
records_list.adapter.notifyItemRemoved(viewHolder.adapterPosition)
}
}
}
val itemTouchHelper = ItemTouchHelper(touchListner)
itemTouchHelper.attachToRecyclerView(records_list)
}
I get an object is not abstract error and override does nothing error. I have searched around and tried to reformat the code but cant seem to fix it. Any guidance would be appreciated.
cheers

Ahhh
After alot of looking and pondering i finally worked out, i had to re-impliment the move and swipe methods.
this fixed the issue
cheers

Related

TornadoFX:proper way to bind model

I was taking a look at this :
tornadofx
and tried to expand on it with database connection and little more options, (not all of them make sense, but its just playing in a sandbox).
Even though table can be directly edited and the data will persist in database, i did try to do edit through text fields too. actual table editing would happen through different view and not table itself, as i said its just example.
Database used is Jetbrains Exposed.
object Categories : IntIdTable() {
val name = varchar("name", 64).uniqueIndex()
val description = varchar("description", 128)
}
class Category(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<Category>(Categories)
var name by Categories.name
var description by Categories.description
override fun toString(): String {
return "Category(name=\"$name\", description=\"$description\")"
}
}
now controller looks something like this, functions are just rudimentary and picked as an example.
typealias ModelToDirtyState = Map.Entry<CategoryModel, TableColumnDirtyState<CategoryModel>>
class CategoryModel() : ItemViewModel<Category>() {
val name: SimpleStringProperty = bind(Category::name)
val description: SimpleStringProperty = bind(Category::description)
}
class DBController : Controller() {
val categories: ObservableList<CategoryModel> by lazy {
transaction {
SchemaUtils.create(Categories)
Category.all().map {
CategoryModel().apply {
item = it
}
}.observable()
}
}
init {
Database.connect(
"jdbc:mysql://localhost:3306/test", driver = "com.mysql.cj.jdbc.Driver",
user = "test", password = "test"
)
TransactionManager.manager.defaultIsolationLevel = Connection.TRANSACTION_SERIALIZABLE
}
fun deleteCategory(model: CategoryModel) {
runAsync {
transaction {
model.item.delete()
}
}
categories.remove(model)
}
fun updateCategory(model: CategoryModel) {
transaction {
Categories.update {
model.commit()
}
}
}
fun commitDirty(modelDirtyMappings: Sequence<ModelToDirtyState>) {
transaction {
modelDirtyMappings.filter { it.value.isDirty }.forEach {
it.key.commit()
println(it.key)// commit value to database
it.value.commit() // clear dirty state
}
}
}
Just to quickly comment on controller, delete method works as "intended" however the update one does not, it does not work in sense that after using delete item is remove both from database and tableview(underlying list) itself, and when i do update its not, now i know the reason, i call remove manually on both database and list, now for update perhaps i could do change listener, or maybe tornadofx can do this for me, i just cant set it up to do it. Following code will make things clearer i think.
class CategoryEditor : View("Categories") {
val categoryModel: CategoryModel by inject()
val dbController: DBController by inject()
var categoryTable: TableViewEditModel<CategoryModel> by singleAssign()
var categories: ObservableList<CategoryModel> by singleAssign()
override val root = borderpane {
categories = dbController.categories
center = vbox {
buttonbar {
button("Commit") {
action {
dbController.commitDirty(categoryTable.items.asSequence())
}
}
button("Roll;back") {
action {
categoryTable.rollback()
}
}
// This model only works when i use categorytable.tableview.selected item, if i use categoryModel, list gets updated but not the view itself
// Question #1 how to use just categoryModel variable without need to use categorytable.tableview.selecteditem
button("Delete ") {
action {
val model = categoryTable.tableView.selectedItem
when (model) {
null -> return#action
else -> dbController.deleteCategory(model)
}
}
}
//And here no matter what i did i could not make the view update
button("Update") {
action {
when (categoryModel) {
null -> return#action
else -> dbController.updateCategory(categoryModel)
}
categoryTable.tableView.refresh()
}
}
}
tableview<CategoryModel> {
categoryTable = editModel
items = categories
enableCellEditing()
enableDirtyTracking()
onUserSelect() {
//open a dialog
}
//DOES WORK
categoryModel.rebindOnChange(this) { selectedItem ->
item = selectedItem?.item ?: CategoryModel().item
}
// Question #2. why bindSelected does not work, and i have to do it like above
//DOES NOT WORK
// bindSelected(categoryModel)
//
column("Name", CategoryModel::name).makeEditable()
column("Description", CategoryModel::description).makeEditable()
}
}
right = form {
fieldset {
field("Name") {
textfield(categoryModel.name)
}
}
fieldset {
field("Description") {
textfield(categoryModel.description)
}
}
button("ADD CATEGORY") {
action {
dbController.addCategory(categoryModel.name.value, categoryModel.description.value)
}
}
}
}
}
I apologize for huge amount of code, also in last code snipped i left questions in form of comments where i fail to achive desired results.
I am sure i am not properly binding code, i just dont see why, also i sometimes use one variable to update data, my declared one "categoryModel" and sometimes i use tableview.selecteditem, it just seems hacky and i cant seem to grasp way.
Thank you!

Removing Firestore snapshot listener inside of LiveData-returning function

I'm trying to optimise the performances in my app and I noticed that I do not remove Firestore listeners from my repository.
My repository has a number of functions that return a LiveData, that is then observed via Transformations from ViewModels and then the views.
One-time operations work absolutely fine (upload, delete etc.) but permanent listeners don't get garbage collected when the activity finishes.
Right now the function inside the repository looks like this:
// [...]
class Repository {
// [...]
fun retrieveCode() {
val observable = MutableLiveData<Code>()
val reference =
FirebaseFirestore.getInstance().collection(/**/).document(/**/)
reference
.addSnapshotListener { snapshot, exception ->
if(exception != null) {
observable.value = null
}
if(snapshot != null {
observable.value = snapshot.//[convert to object]
}
}
return observable
}
}
I found a workaround which is to create a custom LiveData object that handles the listener removal when it becomes inactive, like this:
class CodeLiveData(private val reference: DocumentReference):
LiveData<Code>(), EventListener<DocumentSnapshot>{
private var registration: ListenerRegistration? = null
override fun onEvent(snapshot: DocumentSnapshot?,
exception: FirebaseFirestoreException?) {
if(exception != null) {
this.value = null
}
if(snapshot != null) {
this.value = snapshot.//[convert to object]
}
}
override fun onActive() {
super.onActive()
registration = reference.addSnapshotListener(this)
}
override fun onInactive() {
super.onInactive()
registration?.remove()
}
}
Is there a way to solve this problem without creating a custom class, but rather by improving a function similar to the first example?
Thanks,
Emilio
There are two ways in which you can achieve this. The first one would be to stop listening for changes and this can be done in your onStop() function by calling remove() function on your ListenerRegistration object like this:
if (registration != null) {
registration.remove();
}
The approach would be to you pass your activity as the first argument in the addSnapshotListener() function, so Firestore can clean up the listeners automatically when the activity is stopped.
var registration = dataDocumentReference
.addSnapshotListener(yourActivity, listener)

Detect When A Node is Visible in a Scene

I am trying to find a way to detect (or receive notification) that a Node has been added to a Scene and is visible.
I am creating Node objects off the main JavaFx thread and add them to the Stage and Scene using Platform.runLater(). However I would like the Node object to receive notification that is has been added to the Scene and is visible, for example I wish to trigger an animation to start.
I can't seem to find any property or method to add a listener to capture such an event. Any suggestions?
The third-party JavaFX library ReactFX has a mechanism for this, and this exact use case is cited in the blog. In short, you can do
Val<Boolean> showing = Val.flatMap(node.sceneProperty(), Scene::windowProperty)
.flatMap(Window::showingProperty);
and then of course
showing.addListener((obs, wasShowing, isNowShowing) -> {
if (isNowShowing) {
// node is showing
} else {
// node is not showing
}
});
The standard library has a version of this, but it is very badly written. (It is not typesafe, has no compile-time checking that the properties exist, and also pipes a lot of unnecessary warnings to standard error if any of the properties in the "chain" are null, even though the API docs indicate this is a supported use case.) If you want to do this with the standard JavaFX library, you can do
BooleanBinding showing = Bindings.selectBoolean(node.sceneProperty(), "window", "showing");
and then use the binding the same way as above.
Finally, you could do all this by hand, but it gets a bit ugly to manage the listeners properly:
BooleanProperty showing = new SimpleBooleanProperty();
ChangeListener<Window> windowListener = (obs, oldWindow, newWindow) -> {
showing.unbind();
if (newWindow != null) {
showing.bind(newWindow.showingProperty());
} else {
showing.set(false);
}
};
ChangeListener sceneListener = (obs, oldScene, newScene) -> {
showing.unbind();
if (oldScene != null) {
oldScene.windowProperty().removeListener(windowListener);
}
if (newScene == null) {
showing.set(false);
} else {
newScene.windowProperty().addListener(windowListener);
if (newScene.getWindow() == null) {
showing.set(false);
} else {
showing.bind(newScene.getWindow().showingProperty());
}
}
};
node.sceneProperty().addListener(sceneListener);
if (node.getScene() == null) {
showing.set(false);
} else {
node.getScene().windowProperty().add(windowListener);
if (node.getScene().getWindow() == null) {
showing.set(false);
} else {
showing.bind(node.getScene().getWindow().showingProperty());
}
}
You can add a listener to the children property of container node into which you are adding the new node.
grid.getChildren().addListener((ListChangeListener<? super Node>) change -> {
System.out.println(change.getList().get(0).getTypeSelector());
});
change.getList().get(0) returns the first node that is added to grid object.
After James's comment, I have looked up and yes, it is possible to do it from node's perspective as well. You can listen to parentProeprty's changes on the node. Following snippet shows the way to do it.
Button b = new Button("Test");
b.parentProperty().addListener((observable, oldValue, newValue) -> {
System.out.println("added to a container " + newValue);
});
answerPane.getChildren().add(b);

How to access global telerik functions in typescript

I try to access the function GetRadWindowManager() from my app.ts file.
I added the definitions files and I cannot find a way to call "window.GetRadWindowManager()" or "window.top.GetRadWindowManager()"
That is my code until now :
export class App {
private _windowManager: Telerik.Web.UI.RadWindowManager;
constructor() {
this._windowManager = null;
}
getRadWindowManager(): Telerik.Web.UI.RadWindowManager {
if (this._windowManager == null) {
try {
this._windowManager = window.top.GetRadWindowManager();
} catch (err) {
this._windowManager = GetRadWindowManager();
}
}
return this._windowManager;
}
}
PS : Don't mind the try/catch block, I'll remove that later :)
Thanks for your help !
Well, I don't know if this answer is the best, but extending the Window object allowed me to manually add this function. Then I can simply call it from my script.ts
interface Window {
GetRadWindowManager(): Telerik.Web.UI.RadWindowManager;
}

Flex navigateToURL from iframe POST

Let me explain my current issue right now:
I have a webapp located at domain A. Let's call it A-App. I open an iframe from A-App that points to a Flex app on domain B. We'll call it B-FlexApp. B-FlexApp wants to post some data to another app located on the same domain, we'll call it B-App. The problem is that in IE the communication breaks somewhere between B-FlexApp and B-App while B-FlexApp is opened in the iframe. This only happens in IE.
However when opening B-FlexApp in a new window, posting the data to B-App works just fine. How to overcome this? Dropping the iframe is not possible.
ThereĀ“s a issue with AS3 navigateToURL and IE. You can try calling javascript to navigate: I have a little utility class to handle this:
//class URLUtil
package com
{
import flash.external.*;
import flash.net.*;
public class URLUtil extends Object
{
protected static const WINDOW_OPEN_FUNCTION:String="window.open";
public function URLUtil()
{
super();
return;
}
public static function openWindow(arg1:String = "", arg2:String="_blank", arg3:String=""):void
{
var browserName:String = getBrowserName();
switch (browserName)
{
case "Firefox":
{
flash.external.ExternalInterface.call(WINDOW_OPEN_FUNCTION, arg1, arg2, arg3);
break;
}
case "IE":
{
flash.external.ExternalInterface.call("function setWMWindow() {window.open(\'" + arg1 + "\');}");
break;
}
case "Safari":
case "Opera":
{
flash.net.navigateToURL(new URLRequest(arg1), arg2);
break;
}
default:
{
flash.net.navigateToURL(new URLRequest(arg1), arg2);
break;
}
}
return;
}
private static function getBrowserName():String
{
var str:String="";
var browserName:String = ExternalInterface.call("function getBrowser(){return navigator.userAgent;}");
if (!(browserName == null) && browserName.indexOf("Firefox") >= 0)
{
str = "Firefox";
}
else
{
if (!(browserName == null) && browserName.indexOf("Safari") >= 0)
{
str = "Safari";
}
else
{
if (!(browserName == null) && browserName.indexOf("MSIE") >= 0)
{
str = "IE";
}
else
{
if (!(browserName == null) && browserName.indexOf("Opera") >= 0)
{
str = "Opera";
}
else
{
str = "Undefined";
}
}
}
}
trace("Browser: \t" + str);
return str;
}
}
}
and you call it like:
btn.addEventListener(MouseEvent.CLICK, onBTNClick);
function onBTNClick(evt:MouseEvent):void
{
URLUtil.openWindow(YOUR_URL_STRING);
}
Hope it helps!
It is better to let the browser actually does the "navigate to URL" function instead of Flex.
For example, in the page that contains the Flex app, the page would contain a Javascript function call handleNavigationRequest(pageName, target). In the Flex application, you may utilize ExternalInterface, and call the handleNavigationRequest.
By using this paradigm, the Flex application would not have to figure the details as to how the external implementations such as frame setup, etc, and you end up having a cleaner and less-coupled design.
I've found out that i can use swfObject to embed the flash object thus the iframe implementation is completely useless. Embedding the flash component in the overlay, instead of opening it in an iframe, makes IE behave properly.
I had the same problem and I solved it simply passing the second argument (browser window) to the function:
navigateToUrl(url,"_blank"); , in my case I use "_blank".
It works with IE8 and IE9.
Davide

Resources